Compare commits

...

17 Commits

Author SHA1 Message Date
DecDuck cbecd1161d Publish docs, update links (#431)
* Publish docs, update links

* Fix sitemap gen

* Migrate to Astro v6

* Fix server lint
2026-06-21 16:39:34 +10:00
DecDuck 9185089c99 Fix v0.4.0 process handler, add override menu (#430)
* Fix Windows and Linux launch

* Add process handler selector, pin Prisma

* Regenerate lcofkiel

* Fix torrential inclusion in image

* Fix layouting

* Implement tree kill for Windows

* Fix server lint
2026-06-21 15:24:33 +10:00
DecDuck 0290718ee0 Fix droposs.org build, finish website (#429)
* Fix compile issues

* Finish up website
2026-06-21 11:31:21 +10:00
DecDuck 2e86422004 Add lints, new website publish (#428)
* Add lints and new website

* Fix droplet CI

* Fix droplet ci again

* Fix clippy lints
2026-06-21 11:16:39 +10:00
DecDuck 796abf478f Fix GitHub Actions build (#427)
* Fix server build

* Remove server drop-base submod

* Update lockfile

* Use debian images for build

* Fix pino errors, lint

* Fix macOS keychain lookup
2026-06-21 10:37:54 +10:00
dependabot[bot] 062ddc0c24 chore(deps): bump next from 15.4.4 to 15.5.18 in /sites/promo (#413)
Bumps [next](https://github.com/vercel/next.js) from 15.4.4 to 15.5.18.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.4.4...v15.5.18)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 15.5.18
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:13:45 +10:00
dependabot[bot] ac1e0230ae chore(deps): bump nuxt from 3.20.1 to 3.21.6 in /desktop/main (#416)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from 3.20.1 to 3.21.6.
- [Release notes](https://github.com/nuxt/nuxt/releases)
- [Commits](https://github.com/nuxt/nuxt/commits/v3.21.6/packages/nuxt)

---
updated-dependencies:
- dependency-name: nuxt
  dependency-version: 3.21.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:13:01 +10:00
dependabot[bot] 8637ff52ef chore(deps): bump nuxt from 3.21.2 to 3.21.6 (#417)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from 3.21.2 to 3.21.6.
- [Release notes](https://github.com/nuxt/nuxt/releases)
- [Commits](https://github.com/nuxt/nuxt/commits/v3.21.6/packages/nuxt)

---
updated-dependencies:
- dependency-name: nuxt
  dependency-version: 3.21.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:12:37 +10:00
dependabot[bot] 18471a1d35 chore(deps): bump openssl from 0.10.75 to 0.10.80 in /desktop/src-tauri (#419)
Bumps [openssl](https://github.com/rust-openssl/rust-openssl) from 0.10.75 to 0.10.80.
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.75...openssl-v0.10.80)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.80
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:12:17 +10:00
dependabot[bot] c8bb84e0d8 chore(deps): bump tar from 0.4.44 to 0.4.46 in /desktop/src-tauri (#420)
Bumps [tar](https://github.com/composefs/tar-rs) from 0.4.44 to 0.4.46.
- [Release notes](https://github.com/composefs/tar-rs/releases)
- [Commits](https://github.com/composefs/tar-rs/compare/0.4.44...0.4.46)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 0.4.46
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-15 08:11:47 +10:00
dependabot[bot] a3974f6137 chore(deps-dev): bump nitropack from 2.13.3 to 2.13.4 (#410)
Bumps [nitropack](https://github.com/nitrojs/nitro) from 2.13.3 to 2.13.4.
- [Release notes](https://github.com/nitrojs/nitro/releases)
- [Changelog](https://github.com/nitrojs/nitro/blob/main/changelog.config.ts)
- [Commits](https://github.com/nitrojs/nitro/compare/v2.13.3...v2.13.4)

---
updated-dependencies:
- dependency-name: nitropack
  dependency-version: 2.13.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:28:18 +10:00
dependabot[bot] 0b4e20bd0f chore(deps): bump vite from 7.2.2 to 7.3.2 in /desktop/main (#388)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.2.2 to 7.3.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:27:20 +10:00
dependabot[bot] 546f47e40e chore(deps): bump defu from 6.1.4 to 6.1.6 in /desktop/main (#386)
Bumps [defu](https://github.com/unjs/defu) from 6.1.4 to 6.1.6.
- [Release notes](https://github.com/unjs/defu/releases)
- [Changelog](https://github.com/unjs/defu/blob/main/CHANGELOG.md)
- [Commits](https://github.com/unjs/defu/compare/v6.1.4...v6.1.6)

---
updated-dependencies:
- dependency-name: defu
  dependency-version: 6.1.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:26:57 +10:00
dependabot[bot] 516eaade4f chore(deps): bump time from 0.3.44 to 0.3.47 in /torrential (#381)
Bumps [time](https://github.com/time-rs/time) from 0.3.44 to 0.3.47.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.44...v0.3.47)

---
updated-dependencies:
- dependency-name: time
  dependency-version: 0.3.47
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:26:43 +10:00
dependabot[bot] 784e42f177 chore(deps): bump bytes from 1.11.0 to 1.11.1 in /torrential (#380)
Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.11.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:26:27 +10:00
dependabot[bot] 758baa9bbb chore(deps): bump quinn-proto from 0.11.13 to 0.11.14 in /torrential (#379)
Bumps [quinn-proto](https://github.com/quinn-rs/quinn) from 0.11.13 to 0.11.14.
- [Release notes](https://github.com/quinn-rs/quinn/releases)
- [Commits](https://github.com/quinn-rs/quinn/compare/quinn-proto-0.11.13...quinn-proto-0.11.14)

---
updated-dependencies:
- dependency-name: quinn-proto
  dependency-version: 0.11.14
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 12:26:12 +10:00
DecDuck bf7ce5927f Attempt fix monorepo build (#404)
* add latest changes and fix launcher

* add optional tag specify

* fix client release

* empty commit
2026-04-27 15:38:05 +10:00
79 changed files with 7508 additions and 4900 deletions
+6
View File
@@ -0,0 +1,6 @@
/sites
/cli
/desktop
/backend # go backend
node_modules
@@ -1,7 +1,12 @@
name: "publish"
name: "Build and release desktop"
on:
workflow_dispatch: {}
workflow_dispatch:
inputs:
tagName:
required: false
type: string
description: "tagName to be associated with this release."
release:
types: [published]
# This can be used to automatically publish nightlies at UTC nighttime
@@ -33,13 +38,11 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: setup node
@@ -58,7 +61,7 @@ jobs:
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
workspaces: './desktop/src-tauri -> target'
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04' || matrix.platform == 'ubuntu-22.04-arm' # This must match the platform value defined above.
@@ -80,6 +83,10 @@ jobs:
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
# Add build.keychain to the user keychain search list so that codesign
# (invoked later by tauri-action WITHOUT an explicit --keychain) can
# resolve the signing identity from it.
security list-keychains -d user -s build.keychain $(security list-keychains -d user | tr -d '"')
echo "Created keychain"
@@ -115,14 +122,19 @@ jobs:
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
# Do NOT set APPLE_CERTIFICATE / APPLE_CERTIFICATE_PASSWORD here. Doing so
# makes tauri-action import the cert into its own throwaway keychain and
# look up the identity by Apple-only name prefixes (e.g.
# "Developer ID Application:"), which never matches our "Drop OSS" cert
# and fails with "failed to resolve signing identity". Instead we rely on
# the build.keychain prepared above and only pass the resolved identity.
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
NO_STRIP: true
with:
tagName: v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
tagName: ${{ inputs.print_tags || 'v__VERSION__' }} # the action automatically replaces \_\_VERSION\_\_ with the app version.
releaseName: "Auto-release v__VERSION__"
releaseBody: "See the assets to download this version and install. This release was created automatically."
releaseDraft: false
prerelease: true
args: ${{ matrix.args }}
projectPath: './desktop'
+56
View File
@@ -0,0 +1,56 @@
name: Droplet CI
on:
push:
branches: [develop]
paths:
- "libraries/droplet/**"
- "libraries/droplet_types/**"
- "libraries/libarchive/**"
- ".github/workflows/droplet-ci.yml"
pull_request:
branches: [develop]
paths:
- "libraries/droplet/**"
- "libraries/droplet_types/**"
- "libraries/libarchive/**"
- ".github/workflows/droplet-ci.yml"
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
ci:
name: Build, Test, Lint
runs-on: ubuntu-latest
defaults:
run:
working-directory: libraries/droplet
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./libraries/droplet -> target"
- name: Install libarchive
run: |
sudo apt-get update
sudo apt-get install -y libarchive-dev
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run Clippy (lint)
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run tests
run: cargo test --all-features --all --verbose
+100
View File
@@ -0,0 +1,100 @@
name: Deploy website to GitHub Pages
on:
# Runs on pushes targeting the default branch
push:
branches: [develop]
paths:
- "sites/promo/**"
- "sites/docs/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".github/workflows/pages.yml"
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment per the "pages" group, skipping runs queued
# between the in-progress run and the latest queued one. cancel-in-progress defaults
# to false, so in-flight production deployments are allowed to complete.
concurrency: "pages"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "pnpm"
# Only install the promo site (radiant) and docs site (docs-next) and their
# dependencies so the public website deploy stays decoupled from the
# server/desktop build pipelines.
- name: Install dependencies
run: pnpm install --filter radiant... --filter docs-next...
- name: Setup Pages
id: setup_pages
uses: actions/configure-pages@v5
- name: Restore cache
uses: actions/cache@v4
with:
path: |
sites/promo/.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('sites/promo/**.[jt]s', 'sites/promo/**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml') }}-
- name: Build promo site with Next.js
working-directory: sites/promo
run: pnpm run build
env:
PAGES_BASE_PATH: ${{ steps.setup_pages.outputs.base_path }}
- name: Build docs site with Astro
working-directory: sites/docs
run: pnpm run build
# Nest the Starlight docs (built with base: "/docs") inside the promo export
# so both ship from a single GitHub Pages deployment at /docs.
- name: Assemble docs into /docs
run: |
rm -rf sites/promo/out/docs
mkdir -p sites/promo/out/docs
cp -r sites/docs/dist/. sites/promo/out/docs/
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: sites/promo/out
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
@@ -1,12 +1,24 @@
name: CI
name: Server CI
on:
push:
branches:
- develop
branches: [develop]
paths:
- "server/**"
- "libraries/base/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".github/workflows/server-ci.yml"
pull_request:
branches:
- develop
branches: [develop]
paths:
- "server/**"
- "libraries/base/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".github/workflows/server-ci.yml"
permissions:
contents: read
@@ -18,8 +30,6 @@ jobs:
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -34,6 +44,7 @@ jobs:
run: pnpm install
- name: Typecheck
working-directory: server
run: pnpm run typecheck
lint:
@@ -42,8 +53,6 @@ jobs:
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
submodules: true
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -58,4 +67,5 @@ jobs:
run: pnpm install
- name: Lint
working-directory: server
run: pnpm run lint
-3
View File
@@ -29,7 +29,6 @@ jobs:
- name: Check out the repo
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 3 # fix for when this gets triggered by tag
fetch-tags: true
ref: ${{ github.ref }}
@@ -90,8 +89,6 @@ jobs:
build-args: |
BUILD_DROP_VERSION=${{ steps.get_final_ver.outputs.final_ver }}
BUILD_GIT_REF=${{ github.sha }}
context: ./server
file: ./server/Dockerfile
- name: Export digest
run: |
+105
View File
@@ -0,0 +1,105 @@
# syntax=docker/dockerfile:1
# Pinned to bookworm so the glibc here matches the torrential build stage
# and the libarchive runtime package is named `libarchive13` (trixie renames it to libarchive13t64).
FROM node:lts-bookworm-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
## so corepack knows pnpm's version
COPY . .
## prevent prompt to download
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
## setup for offline
RUN corepack pack
## don't call out to network anymore
ENV COREPACK_ENABLE_NETWORK=0
### INSTALL DEPS ONCE
FROM base AS deps
RUN pnpm install --frozen-lockfile --ignore-scripts
### BUILD TORRENTIAL
# Bookworm-pinned to match the runtime image's glibc (a trixie build would not run on bookworm).
FROM rustlang/rust:nightly-bookworm-slim AS torrential-build
## libarchive-dev + pkg-config let libarchive3-sys link libarchive dynamically (glibc).
## protobuf-compiler is kept for parity (torrential's build.rs uses a vendored protoc).
RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
libarchive-dev \
protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
COPY . .
RUN cargo build --release --manifest-path ./torrential/Cargo.toml
### BUILD APP
FROM base AS build-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
## add git so drop can determine its git ref at build
RUN apt-get update && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
## copy deps and rest of project files
COPY . .
COPY --from=deps /app/node_modules ./node_modules
ARG BUILD_DROP_VERSION
ARG BUILD_GIT_REF
## build
RUN pnpm run --filter=drop postinstall && pnpm run --filter=drop build
# create run environment for Drop
FROM base AS run-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
# The base stage's `COPY . .` puts the whole repo into the runtime WORKDIR (/app),
# but at runtime only the artifacts copied explicitly below are needed. Drop the
# inherited `torrential` source dir: the service resolves the binary by scanning
# the cwd for `torrential`, and a directory there is spawned as ./torrential and
# fails with EACCES. With it gone, resolution falls through to the `torrential`
# binary installed on PATH (/usr/bin/torrential) below.
RUN rm -rf /app/torrential
# RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn add --network-timeout 1000000 --no-lockfile --ignore-scripts prisma@6.11.1
## runtime deps:
## - libarchive13: torrential now links libarchive dynamically (glibc build)
## - p7zip-full: provides the 7z CLI
## - nginx: front-end proxy
## - openssl + ca-certificates: required by Prisma's query engine on Debian
## pnpm itself is provided by corepack (enabled in the base stage)
RUN apt-get update && apt-get install -y --no-install-recommends \
libarchive13 \
p7zip-full \
nginx \
openssl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN pnpm install prisma@7.7.0 --global
# init prisma to download all required files
RUN pnpm prisma init
COPY --from=build-system /app/server/prisma.config.ts ./
COPY --from=build-system /app/server/.output ./app
COPY --from=build-system /app/server/prisma ./prisma
COPY --from=build-system /app/server/build ./startup
COPY --from=build-system /app/server/build/nginx.conf /nginx.conf
COPY --from=torrential-build /build/torrential/target/release/torrential /usr/bin/
ENV LIBRARY="/library"
ENV DATA="/data"
ENV NGINX_CONFIG="/nginx.conf"
# Nuxt's port
ENV PORT=4000
CMD ["sh", "/app/startup/launch.sh"]
+2 -2
View File
@@ -6,7 +6,7 @@
# Drop
[![Website](https://img.shields.io/badge/website-000000?style=for-the-badge&logo=About.me&logoColor=white)](https://droposs.org)
[![Docs](https://img.shields.io/badge/DOCS-black?style=for-the-badge&logo=docusaurus)](https://docs.droposs.org/)
[![Docs](https://img.shields.io/badge/DOCS-black?style=for-the-badge&logo=docusaurus)](https://droposs.org/docs)
[![Static Badge](https://img.shields.io/badge/FORUM-blue?style=for-the-badge)](https://forum.droposs.org)
[![GitHub License](https://img.shields.io/badge/AGPL--3.0-red?style=for-the-badge)](LICENSE)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/ACq4qZp4a9)
@@ -28,7 +28,7 @@ Drop is an open-source game distribution platform, similar to GameVault or Steam
## Deployment
See our documentation on how to [deploy Drop](https://docs.droposs.org/docs/guides/quickstart) for more information.
See our documentation on how to [deploy Drop](https://droposs.org/docs/admin/quickstart) for more information.
## Contributing
-31
View File
@@ -1,31 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Arch Linux, Windows]
- App Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-23
View File
@@ -1,23 +0,0 @@
on: push
name: Clippy check
jobs:
clippy_check:
runs-on: ubuntu-24.04
permissions:
checks: write
steps:
- uses: actions/checkout@v1
- name: install dependencies (ubuntu only)
run: |
sudo apt-get update
sudo apt-get install -y libglib2.0-dev libgtk-3-dev libwebkit2gtk-4.1-dev
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path ./src-tauri/Cargo.toml
@@ -0,0 +1,141 @@
<template>
<Listbox
as="div"
v-model="model.overrideHandler"
class="mt-6"
v-if="handlers.length > 1"
>
<ListboxLabel class="block text-sm/6 font-medium text-white"
>Launch method</ListboxLabel
>
<div class="relative mt-2">
<ListboxButton
class="grid w-full cursor-default grid-cols-1 rounded-md bg-white/5 py-1.5 pr-2 pl-3 text-left text-white outline-1 -outline-offset-1 outline-white/10 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-blue-500 sm:text-sm/6"
>
<span
v-if="currentHandler"
class="col-start-1 row-start-1 truncate pr-6"
>{{ currentHandler.name }}</span
>
<span
v-else
class="col-start-1 row-start-1 truncate pr-6 italic text-zinc-400"
>Automatic</span
>
<ChevronUpDownIcon
class="col-start-1 row-start-1 size-5 self-center justify-self-end text-zinc-400 sm:size-4"
aria-hidden="true"
/>
</ListboxButton>
<transition
leave-active-class="transition ease-in duration-100"
leave-from-class=""
leave-to-class="opacity-0"
>
<ListboxOptions
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-800 py-1 text-base outline-1 -outline-offset-1 outline-white/10 sm:text-sm"
>
<ListboxOption
as="template"
:value="undefined"
v-slot="{ active, selected }"
>
<li
:class="[
active ? 'bg-blue-500 text-white outline-hidden' : 'text-white',
'relative cursor-default py-2 pr-9 pl-3 select-none',
]"
>
<span
:class="[
selected ? 'font-semibold' : 'font-normal',
'block truncate italic',
]"
>Automatic</span
>
<span class="block truncate text-xs text-zinc-400"
>Pick the best method for this game.</span
>
<span
v-if="selected"
:class="[
active ? 'text-white' : 'text-blue-400',
'absolute inset-y-0 right-0 flex items-center pr-4',
]"
>
<CheckIcon class="size-5" aria-hidden="true" />
</span>
</li>
</ListboxOption>
<ListboxOption
as="template"
v-for="handler in handlers"
:key="handler.id"
:value="handler.id"
v-slot="{ active, selected }"
>
<li
:class="[
active ? 'bg-blue-500 text-white outline-hidden' : 'text-white',
'relative cursor-default py-2 pr-9 pl-3 select-none',
]"
>
<span
:class="[
selected ? 'font-semibold' : 'font-normal',
'block truncate',
]"
>{{ handler.name }}</span
>
<span class="block truncate text-xs text-zinc-400">{{
handler.description
}}</span>
<span
v-if="selected"
:class="[
active ? 'text-white' : 'text-blue-400',
'absolute inset-y-0 right-0 flex items-center pr-4',
]"
>
<CheckIcon class="size-5" aria-hidden="true" />
</span>
</li>
</ListboxOption>
</ListboxOptions>
</transition>
</div>
<p class="mt-2 text-sm text-zinc-400">
Override how this game is launched.
</p>
</Listbox>
</template>
<script setup lang="ts">
import { invoke } from "@tauri-apps/api/core";
import {
Listbox,
ListboxButton,
ListboxLabel,
ListboxOption,
ListboxOptions,
} from "@headlessui/vue";
import { ChevronUpDownIcon } from "@heroicons/vue/16/solid";
import { CheckIcon } from "@heroicons/vue/20/solid";
import type { GameVersion } from "~/types";
type ProcessHandlerOption = { id: string; name: string; description: string };
const model = defineModel<GameVersion["userConfiguration"]>({ required: true });
const props = defineProps<{ gameId: string }>();
const handlers = await invoke<ProcessHandlerOption[]>("get_process_handlers", {
id: props.gameId,
});
const currentHandler = computed(() =>
handlers.find((v) => v.id == model.value.overrideHandler),
);
</script>
@@ -23,16 +23,19 @@
</p>
<ProtonSelector v-model="model" v-if="$props.protonEnabled" />
<HandlerSelector v-model="model" :game-id="$props.gameId" />
</div>
</template>
<script setup lang="ts">
import type { GameVersion } from "~/types";
import ProtonSelector from "./ProtonSelector.vue";
import HandlerSelector from "./HandlerSelector.vue";
const model = defineModel<GameVersion["userConfiguration"]>({ required: true });
const props = defineProps<{
protonEnabled: boolean;
gameId: string;
}>();
</script>
+3 -2
View File
@@ -1,7 +1,7 @@
<template>
<ModalTemplate size-class="max-w-4xl" v-model="open">
<template #default>
<div class="flex flex-row gap-x-4 h-96">
<div class="flex flex-row gap-x-4 min-h-96">
<nav class="flex flex-1 flex-col" aria-label="Sidebar">
<ul role="list" class="-mx-2 space-y-1">
<li v-for="(tab, tabIdx) in tabs" :key="tab.name">
@@ -29,11 +29,12 @@
</li>
</ul>
</nav>
<div class="border-l-2 border-zinc-800 w-full grow pl-4 overflow-y-scroll">
<div class="border-l-2 border-zinc-800 w-full grow pl-4">
<component
v-model="configuration"
:is="tabs[currentTabIndex]?.page"
:proton-enabled="protonEnabled"
:game-id="props.gameId"
/>
</div>
</div>
+1 -1
View File
@@ -22,7 +22,7 @@
"koa": "^2.16.1",
"markdown-it": "^14.1.0",
"micromark": "^4.0.1",
"nuxt": "^3.16.0",
"nuxt": "^4.4.8",
"scss": "^0.2.4",
"vue-router": "latest",
"vuedraggable": "^4.1.0"
+2957 -2333
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -53,6 +53,7 @@ export type GameVersion = {
userConfiguration: {
launchTemplate: string;
overrideProtonPath: string;
overrideHandler: string | undefined;
enableUpdates: boolean
};
setups: Array<{ platform: string }>;
+1 -1
View File
@@ -7,7 +7,7 @@
"tauri": "tauri"
},
"dependencies": {
"pino": "^9.7.0",
"pino": "^9.14.0",
"pino-pretty": "^13.1.1",
"tauri": "^0.15.0"
},
+7 -7
View File
@@ -1392,6 +1392,7 @@ dependencies = [
"http-serde 2.1.1",
"humansize",
"known-folders",
"libloading",
"log",
"log4rs",
"md5 0.7.0",
@@ -3932,15 +3933,14 @@ dependencies = [
[[package]]
name = "openssl"
version = "0.10.75"
version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
@@ -3970,9 +3970,9 @@ checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391"
[[package]]
name = "openssl-sys"
version = "0.9.111"
version = "0.9.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
dependencies = [
"cc",
"libc",
@@ -6241,9 +6241,9 @@ dependencies = [
[[package]]
name = "tar"
version = "0.4.44"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
+4 -1
View File
@@ -82,7 +82,7 @@ sha1 = "0.10.6"
shared_child = "1.0.1"
slice-deque = "0.3.0"
sysinfo = "0.36.1"
tar = "0.4.44"
tar = "0.4.46"
tauri-plugin-autostart = "*"
tauri-plugin-deep-link = "*"
tauri-plugin-dialog = "*"
@@ -136,6 +136,9 @@ tauri-build = { version = "*", features = [] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies]
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
[target."cfg(target_os = \"linux\")".dependencies]
libloading = "0.7"
[profile.release]
lto = true
panic = "abort"
+1 -1
View File
@@ -12,7 +12,7 @@ rustix = "1.1.2"
serde = "1.0.228"
serde_json = "1.0.145"
serde_with = "3.15.0"
tar = "0.4.44"
tar = "0.4.46"
tempfile = "3.23.0"
uuid = "1.18.1"
whoami = "1.6.1"
+3
View File
@@ -79,6 +79,7 @@ pub mod data {
UserConfiguration {
launch_template: "{}".to_owned(),
override_proton_path: None,
override_handler: None,
enable_updates: false,
}
}
@@ -88,6 +89,8 @@ pub mod data {
pub struct UserConfiguration {
pub launch_template: String,
pub override_proton_path: Option<String>,
#[serde(default)]
pub override_handler: Option<String>,
pub enable_updates: bool,
}
@@ -1,11 +1,15 @@
use std::{fs::create_dir_all, path::PathBuf, process::Command};
use std::{
fs::create_dir_all,
path::{Path, PathBuf},
process::Command,
};
use client::compat::{COMPAT_INFO, UMU_LAUNCHER_EXECUTABLE};
use database::{
Database, DownloadableMetadata, GameVersion, db::DATA_ROOT_DIR, platform::Platform,
};
use crate::{error::ProcessError, process_manager::ProcessHandler};
use crate::{error::ProcessError, parser::ParsedCommand, process_manager::ProcessHandler};
pub struct MacLauncher;
impl ProcessHandler for MacLauncher {
@@ -25,11 +29,89 @@ impl ProcessHandler for MacLauncher {
}
fn modify_command(&self, _command: &mut Command) {}
fn id(&self) -> &'static str {
"macos"
}
fn name(&self) -> &'static str {
"Direct"
}
fn description(&self) -> &'static str {
"Launches the game directly on macOS."
}
}
#[allow(dead_code)]
const CREATE_NO_WINDOW: u32 = 0x08000000;
#[cfg_attr(not(target_os = "windows"), allow(unused_variables))]
fn apply_no_window(command: &mut Command) {
#[cfg(target_os = "windows")]
{
use std::os::windows::process::CommandExt;
command.creation_flags(CREATE_NO_WINDOW);
}
}
enum WindowsLaunchStrategy {
Direct,
Cmd,
Powershell,
}
// Wrap a launch command for Windows; with no strategy, detect it from the file extension.
fn windows_launch_command(
launch_command: String,
current_dir: &str,
strategy: Option<WindowsLaunchStrategy>,
) -> Result<String, ProcessError> {
let mut parsed = ParsedCommand::parse(launch_command)?;
let strategy = strategy.unwrap_or_else(|| {
let extension = Path::new(&parsed.command)
.extension()
.and_then(|ext| ext.to_str())
.map(str::to_ascii_lowercase);
match extension.as_deref() {
Some("ps1") => WindowsLaunchStrategy::Powershell,
Some("exe") | Some("com") => WindowsLaunchStrategy::Direct,
_ => WindowsLaunchStrategy::Cmd,
}
});
match strategy {
// PowerShell scripts
WindowsLaunchStrategy::Powershell => {
parsed.make_absolute(PathBuf::from(current_dir));
let script = std::mem::replace(&mut parsed.command, "powershell".to_owned());
let mut args = vec![
"-NoProfile".to_owned(),
"-ExecutionPolicy".to_owned(),
"Bypass".to_owned(),
"-File".to_owned(),
script,
];
args.append(&mut parsed.args);
parsed.args = args;
}
// Direct executables
WindowsLaunchStrategy::Direct => {
parsed.make_absolute(PathBuf::from(current_dir));
}
// cmd.exe, for batch files, builtins, PATHEXT resolution, %VAR% expansion, etc.
WindowsLaunchStrategy::Cmd => {
let command = std::mem::replace(&mut parsed.command, "cmd".to_owned());
let mut args = vec!["/C".to_owned(), command];
args.append(&mut parsed.args);
parsed.args = args;
}
}
Ok(parsed.reconstruct())
}
pub struct WindowsLauncher;
impl ProcessHandler for WindowsLauncher {
fn create_launch_process(
@@ -37,22 +119,169 @@ impl ProcessHandler for WindowsLauncher {
_meta: &DownloadableMetadata,
launch_command: String,
_game_version: &GameVersion,
_current_dir: &str,
current_dir: &str,
_database: &Database,
) -> Result<String, ProcessError> {
Ok(format!("cmd /C \"{}\"", launch_command))
windows_launch_command(launch_command, current_dir, None)
}
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
true
}
#[allow(unused_variables)]
fn modify_command(&self, command: &mut Command) {
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
#[cfg(target_os = "windows")]
command.creation_flags(CREATE_NO_WINDOW);
apply_no_window(command);
}
fn id(&self) -> &'static str {
"windows-auto"
}
fn name(&self) -> &'static str {
"Automatic"
}
fn description(&self) -> &'static str {
"Detects the file type and launches it directly, or through cmd or PowerShell."
}
}
pub struct WindowsDirectLauncher;
impl ProcessHandler for WindowsDirectLauncher {
fn create_launch_process(
&self,
_meta: &DownloadableMetadata,
launch_command: String,
_game_version: &GameVersion,
current_dir: &str,
_database: &Database,
) -> Result<String, ProcessError> {
windows_launch_command(launch_command, current_dir, Some(WindowsLaunchStrategy::Direct))
}
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
true
}
fn modify_command(&self, command: &mut Command) {
apply_no_window(command);
}
fn id(&self) -> &'static str {
"windows-direct"
}
fn name(&self) -> &'static str {
"Direct executable"
}
fn description(&self) -> &'static str {
"Runs the executable directly, without a shell."
}
}
pub struct WindowsCmdLauncher;
impl ProcessHandler for WindowsCmdLauncher {
fn create_launch_process(
&self,
_meta: &DownloadableMetadata,
launch_command: String,
_game_version: &GameVersion,
current_dir: &str,
_database: &Database,
) -> Result<String, ProcessError> {
windows_launch_command(launch_command, current_dir, Some(WindowsLaunchStrategy::Cmd))
}
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
true
}
fn modify_command(&self, command: &mut Command) {
apply_no_window(command);
}
fn id(&self) -> &'static str {
"windows-cmd"
}
fn name(&self) -> &'static str {
"Command Prompt (cmd)"
}
fn description(&self) -> &'static str {
"Launches through cmd.exe. Supports batch files, builtins and %VAR% expansion."
}
}
pub struct WindowsPowershellLauncher;
impl ProcessHandler for WindowsPowershellLauncher {
fn create_launch_process(
&self,
_meta: &DownloadableMetadata,
launch_command: String,
_game_version: &GameVersion,
current_dir: &str,
_database: &Database,
) -> Result<String, ProcessError> {
windows_launch_command(
launch_command,
current_dir,
Some(WindowsLaunchStrategy::Powershell),
)
}
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
true
}
fn modify_command(&self, command: &mut Command) {
apply_no_window(command);
}
fn id(&self) -> &'static str {
"windows-powershell"
}
fn name(&self) -> &'static str {
"PowerShell"
}
fn description(&self) -> &'static str {
"Runs the command as a PowerShell script (-File)."
}
}
pub struct LinuxNativeLauncher;
impl ProcessHandler for LinuxNativeLauncher {
fn create_launch_process(
&self,
_meta: &DownloadableMetadata,
launch_command: String,
_game_version: &GameVersion,
_current_dir: &str,
_database: &Database,
) -> Result<String, ProcessError> {
// Run native Linux games directly, no umu-run wrapper
Ok(launch_command)
}
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
true
}
fn modify_command(&self, _command: &mut Command) {}
fn id(&self) -> &'static str {
"linux-native"
}
fn name(&self) -> &'static str {
"Native (direct)"
}
fn description(&self) -> &'static str {
"Runs the native Linux game directly on the host."
}
}
@@ -101,6 +330,18 @@ impl ProcessHandler for UMUNativeLauncher {
}
fn modify_command(&self, _command: &mut Command) {}
fn id(&self) -> &'static str {
"linux-umu"
}
fn name(&self) -> &'static str {
"Steam Linux Runtime (umu-run)"
}
fn description(&self) -> &'static str {
"Runs the native Linux game inside umu-run's Steam Linux Runtime."
}
}
pub struct UMUCompatLauncher;
@@ -168,6 +409,18 @@ impl ProcessHandler for UMUCompatLauncher {
}
fn modify_command(&self, _command: &mut Command) {}
fn id(&self) -> &'static str {
"proton-umu"
}
fn name(&self) -> &'static str {
"Proton (umu-run)"
}
fn description(&self) -> &'static str {
"Runs the Windows game through Proton using umu-run."
}
}
pub struct AsahiMuvmLauncher;
@@ -228,4 +481,16 @@ impl ProcessHandler for AsahiMuvmLauncher {
}
fn modify_command(&self, _command: &mut Command) {}
fn id(&self) -> &'static str {
"proton-muvm"
}
fn name(&self) -> &'static str {
"Proton + muvm (Asahi)"
}
fn description(&self) -> &'static str {
"Runs through Proton inside a muvm microVM, for Apple Silicon / Asahi Linux."
}
}
@@ -28,7 +28,8 @@ use crate::{
format::DropFormatArgs,
parser::{LaunchParameters, ParsedCommand},
process_handlers::{
AsahiMuvmLauncher, MacLauncher, UMUCompatLauncher, UMUNativeLauncher, WindowsLauncher,
AsahiMuvmLauncher, LinuxNativeLauncher, MacLauncher, UMUCompatLauncher, UMUNativeLauncher,
WindowsCmdLauncher, WindowsDirectLauncher, WindowsLauncher, WindowsPowershellLauncher,
},
};
@@ -54,6 +55,13 @@ pub struct LaunchOption {
name: String,
}
#[derive(Serialize)]
pub struct ProcessHandlerOption {
id: String,
name: String,
description: String,
}
impl ProcessManager<'_> {
pub fn new(app_handle: AppHandle) -> Self {
let log_output_dir = DATA_ROOT_DIR.join("logs");
@@ -76,6 +84,22 @@ impl ProcessManager<'_> {
(Platform::Windows, Platform::Windows),
&WindowsLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
),
(
(Platform::Windows, Platform::Windows),
&WindowsDirectLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
),
(
(Platform::Windows, Platform::Windows),
&WindowsCmdLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
),
(
(Platform::Windows, Platform::Windows),
&WindowsPowershellLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
),
(
(Platform::Linux, Platform::Linux),
&LinuxNativeLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
),
(
(Platform::Linux, Platform::Linux),
&UMUNativeLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
@@ -101,7 +125,7 @@ impl ProcessManager<'_> {
match self.processes.get_mut(&game_id) {
Some(process) => {
process.manually_killed = true;
process.handle.kill()?;
kill_process_tree(&process.handle)?;
let exit_status = process.handle.wait()?;
info!("exit status: {:?}", exit_status);
Ok(())
@@ -188,7 +212,21 @@ impl ProcessManager<'_> {
&self,
db_lock: &Database,
target_platform: &Platform,
override_id: Option<&str>,
) -> Result<&(dyn ProcessHandler + Send + Sync), ProcessError> {
// An explicit override wins, as long as it's valid for the current platform.
if let Some(override_id) = override_id
&& let Some(handler) = self.game_launchers.iter().find(|e| {
let (e_current, e_target) = e.0;
e_current == self.current_platform
&& e_target == *target_platform
&& e.1.id() == override_id
&& e.1.valid_for_platform(db_lock, target_platform)
})
{
return Ok(handler.1);
}
Ok(self
.game_launchers
.iter()
@@ -204,10 +242,44 @@ impl ProcessManager<'_> {
pub fn valid_platform(&self, platform: &Platform) -> bool {
let db_lock = borrow_db_checked();
let process_handler = self.fetch_process_handler(&db_lock, platform);
let process_handler = self.fetch_process_handler(&db_lock, platform, None);
process_handler.is_ok()
}
pub fn get_process_handlers(
&self,
game_id: String,
) -> Result<Vec<ProcessHandlerOption>, ProcessError> {
let db_lock = borrow_db_checked();
let meta = db_lock
.applications
.installed_game_version
.get(&game_id)
.cloned()
.ok_or(ProcessError::NotInstalled)?;
let target_platform = meta.target_platform;
let handlers = self
.game_launchers
.iter()
.filter(|e| {
let (e_current, e_target) = e.0;
e_current == self.current_platform
&& e_target == target_platform
&& e.1.valid_for_platform(&db_lock, &target_platform)
})
.map(|e| ProcessHandlerOption {
id: e.1.id().to_string(),
name: e.1.name().to_string(),
description: e.1.description().to_string(),
})
.collect();
Ok(handlers)
}
pub fn get_launch_options(game_id: String) -> Result<Vec<LaunchOption>, ProcessError> {
let db_lock = borrow_db_checked();
@@ -310,7 +382,12 @@ impl ProcessManager<'_> {
let target_platform = meta.target_platform;
let process_handler = self.fetch_process_handler(&db_lock, &target_platform)?;
let process_handler = self.fetch_process_handler(
&db_lock,
&target_platform,
game_version.user_configuration.override_handler.as_deref(),
)?;
debug!("using process handler {:?}", process_handler.id());
let (target_command, emulator) = match game_status {
GameDownloadStatus::Installed {
@@ -516,6 +593,30 @@ impl ProcessManager<'_> {
}
}
fn kill_process_tree(handle: &SharedChild) -> io::Result<()> {
#[cfg(target_os = "windows")]
{
// handle.kill() only terminates the launched process (often a cmd or
// powershell wrapper), orphaning the actual game. taskkill /T kills the
// whole process tree.
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
let pid = handle.id().to_string();
let killed = Command::new("taskkill")
.args(["/F", "/T", "/PID", pid.as_str()])
.creation_flags(CREATE_NO_WINDOW)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|status| status.success())
.unwrap_or(false);
if killed {
return Ok(());
}
}
handle.kill()
}
pub trait ProcessHandler: Send + 'static {
fn create_launch_process(
&self,
@@ -529,4 +630,8 @@ pub trait ProcessHandler: Send + 'static {
fn valid_for_platform(&self, db: &Database, target: &Platform) -> bool;
fn modify_command(&self, command: &mut Command);
fn id(&self) -> &'static str;
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
}
+50 -10
View File
@@ -8,8 +8,17 @@
#![deny(clippy::all)]
use std::{
env, fs::File, io::Write, panic::PanicHookInfo, path::Path, str::FromStr,
sync::nonpoison::Mutex, time::SystemTime,
env,
fs::File,
io::Write,
panic::PanicHookInfo,
path::Path,
str::FromStr,
sync::{
atomic::{AtomicBool, Ordering},
nonpoison::Mutex,
},
time::SystemTime,
};
use ::client::{
@@ -260,6 +269,7 @@ pub fn run() {
get_autostart_enabled,
open_process_logs,
get_launch_options,
get_process_handlers,
#[cfg(target_os = "linux")]
::process::compat::fetch_proton_paths,
#[cfg(target_os = "linux")]
@@ -359,8 +369,17 @@ pub fn run() {
)
.expect("Failed to generate menu");
if env::var("NO_TRAY_ICON").is_ok_and(|value| value.to_lowercase() == "true") {
TRAY_DISABLED.store(true, Ordering::Relaxed);
} else if !tray_icon_supported() {
warn!(
"appindicator library not available at runtime, disabling system tray icon"
);
TRAY_DISABLED.store(true, Ordering::Relaxed);
}
run_on_tray(|| {
TrayIconBuilder::new()
let tray = TrayIconBuilder::new()
.icon(
app.default_window_icon()
.expect("Failed to get default window icon")
@@ -383,8 +402,12 @@ pub fn run() {
warn!("menu event not handled: {:?}", event.id);
}
})
.build(app)
.expect("error while setting up tray menu");
.build(app);
if let Err(e) = tray {
warn!("failed to set up system tray icon, disabling tray: {e}");
TRAY_DISABLED.store(true, Ordering::Relaxed);
}
});
{
@@ -445,13 +468,30 @@ pub fn run() {
});
}
static TRAY_DISABLED: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "linux")]
fn tray_icon_supported() -> bool {
[
"libayatana-appindicator3.so.1",
"libappindicator3.so.1",
"libayatana-appindicator3.so",
"libappindicator3.so",
]
.iter()
.any(|name| unsafe { libloading::Library::new(name) }.is_ok())
}
#[cfg(not(target_os = "linux"))]
fn tray_icon_supported() -> bool {
true
}
fn run_on_tray<T: FnOnce()>(f: T) {
if match std::env::var("NO_TRAY_ICON") {
Ok(s) => s.to_lowercase() != "true",
Err(_) => true,
} {
(f)();
if TRAY_DISABLED.load(Ordering::Relaxed) {
return;
}
(f)();
}
// TODO: Refactor
+6 -1
View File
@@ -3,7 +3,7 @@ use std::sync::Arc;
use process::{
PROCESS_MANAGER,
error::ProcessError,
process_manager::{LaunchOption, ProcessManager},
process_manager::{LaunchOption, ProcessHandlerOption, ProcessManager},
};
use serde::Serialize;
use tauri::AppHandle;
@@ -16,6 +16,11 @@ pub fn get_launch_options(id: String) -> Result<Vec<LaunchOption>, ProcessError>
Ok(launch_options)
}
#[tauri::command]
pub fn get_process_handlers(id: String) -> Result<Vec<ProcessHandlerOption>, ProcessError> {
PROCESS_MANAGER.lock().get_process_handlers(id)
}
#[derive(Serialize)]
#[serde(tag = "result", content = "data")]
pub enum LaunchResult {
Submodule desktop/src-tauri/tailscale/libtailscale deleted from 78294ac1d6
+1 -1
View File
@@ -14,7 +14,7 @@
"devDependencies": {
"@nuxt/eslint": "latest",
"eslint": "^9.17.0",
"nuxt": "^3.14.1592",
"nuxt": "^3.21.6",
"typescript": "^5.7.2",
"vue": "latest"
},
-53
View File
@@ -1,53 +0,0 @@
name: Rust CI
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
env:
CARGO_TERM_COLOR: always
jobs:
ci:
name: Build, Test, Lint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 3 # fix for when this gets triggered by tag
fetch-tags: true
ref: ${{ github.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install libarchive
run: |
sudo apt-get install libarchive-dev -y
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run Clippy (lint)
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run tests
run: cargo test --all-features --all --verbose
-2
View File
@@ -1,7 +1,5 @@
#![deny(clippy::all)]
#![feature(impl_trait_in_bindings)]
#![feature(nonpoison_mutex)]
#![feature(sync_nonpoison)]
pub mod file_utils;
pub mod manifest;
pub mod ssl;
+1 -1
View File
@@ -1,6 +1,6 @@
use std::{env, path::PathBuf};
use droplet_rs::manifest::{ManifestWriterFactory, generate_manifest_rusty};
use droplet_rs::manifest::{generate_manifest_rusty, ManifestWriterFactory};
use tokio::runtime::Handle;
struct SinkFactory {}
+2 -3
View File
@@ -2,6 +2,7 @@ use std::{collections::HashMap, ops::Not, path::Path};
use anyhow::anyhow;
use async_trait::async_trait;
pub use droplet_types::{ChunkData, FileEntry, Manifest};
use futures::StreamExt;
use hex::ToHex as _;
use humansize::{format_size, BINARY};
@@ -9,8 +10,6 @@ use sha2::{Digest as _, Sha256};
use tokio::io::AsyncWriteExt;
use tokio::io::{AsyncReadExt as _, AsyncWrite};
use tokio::sync::Semaphore;
pub use droplet_types::{ChunkData, FileEntry, Manifest};
pub const CHUNK_SIZE: u64 = 1024 * 1024 * 64;
pub const MAX_FILE_COUNT: usize = 512;
@@ -44,7 +43,7 @@ where
"Could not create backend for path. Is this structure supported?"
))?()?;
let mut files = backend.list_files().await?;
files.sort_by(|a, b| b.size.cmp(&a.size));
files.sort_by_key(|b| std::cmp::Reverse(b.size));
log_sfn("organising files into chunks...".to_string());
+6 -4
View File
@@ -34,9 +34,11 @@ const SUPPORTED_FILE_EXTENSIONS: [&str; 11] = [
];
pub mod types;
pub fn create_backend_constructor<'a, P>(
path: P,
) -> Option<Box<dyn FnOnce() -> Result<Box<dyn VersionBackend + Send + Sync + 'a>>>>
type BackendConstructor<'a> =
Box<dyn FnOnce() -> Result<Box<dyn VersionBackend + Send + Sync + 'a>>>;
pub fn create_backend_constructor<'a, P>(path: P) -> Option<BackendConstructor<'a>>
where
P: AsRef<Path>,
{
@@ -53,7 +55,7 @@ where
}));
};
let file_extension = path.extension().map(|v| v.to_str()).flatten()?;
let file_extension = path.extension().and_then(|v| v.to_str())?;
if SUPPORTED_FILE_EXTENSIONS.contains(&file_extension) {
let buf = path.to_path_buf();
return Some(Box::new(move || Ok(Box::new(ZipVersionBackend::new(buf)?))));
-1
View File
@@ -24,4 +24,3 @@ pub struct Manifest {
pub size: u64,
pub key: [u8; 16],
}
+3394 -1957
View File
File diff suppressed because it is too large Load Diff
-158
View File
@@ -1,158 +0,0 @@
name: Release Workflow
on:
workflow_dispatch: {}
release:
types: [published]
# This can be used to automatically publish nightlies at UTC nighttime
schedule:
- cron: "0 2 * * *" # run at 2 AM UTC
env:
REGISTRY_IMAGE: ghcr.io/drop-oss/drop
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
packages: write
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 3 # fix for when this gets triggered by tag
fetch-tags: true
ref: ${{ github.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Determine final version
id: get_final_ver
run: |
BASE_VER=v$(jq -r '.version' package.json)
TODAY=$(date +'%Y.%m.%d')
echo "Today will be: $TODAY"
echo "today=$TODAY" >> $GITHUB_OUTPUT
if [[ "${{ github.event_name }}" == "release" ]]; then
FINAL_VER="$BASE_VER"
else
FINAL_VER="${BASE_VER}-nightly.$TODAY"
fi
echo "Drop's release tag will be: $FINAL_VER"
echo "final_ver=$FINAL_VER" >> $GITHUB_OUTPUT
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ env.REGISTRY_IMAGE }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
provenance: mode=max
sbom: true
build-args: |
BUILD_DROP_VERSION=${{ steps.get_final_ver.outputs.final_ver }}
BUILD_GIT_REF=${{ github.sha }}
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
permissions:
packages: write
contents: read
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/drop-OSS/drop
tags: |
type=schedule,pattern=nightly
type=schedule,pattern=nightly.${{ steps.get_final_ver.outputs.today }}
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
type=ref,event=branch,prefix=branch-
type=ref,event=pr
type=sha
# set latest tag for stable releases
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.release.prerelease == false }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
-75
View File
@@ -1,75 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:lts-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
## so corepack knows pnpm's version
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
## prevent prompt to download
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
## setup for offline
RUN corepack pack
## don't call out to network anymore
ENV COREPACK_ENABLE_NETWORK=0
### INSTALL DEPS ONCE
FROM base AS deps
RUN pnpm install --frozen-lockfile --ignore-scripts
### BUILD TORRENTIAL
FROM rustlang/rust:nightly-alpine AS torrential-build
RUN apk add musl-dev
WORKDIR /build
COPY torrential .
RUN apk add protoc
RUN cargo build --release
### BUILD APP
FROM base AS build-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
## add git so drop can determine its git ref at build
RUN apk add --no-cache git
## copy deps and rest of project files
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG BUILD_DROP_VERSION
ARG BUILD_GIT_REF
## build
RUN pnpm run postinstall && pnpm run build
# create run environment for Drop
FROM base AS run-system
ENV NODE_ENV=production
ENV NUXT_TELEMETRY_DISABLED=1
# RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn add --network-timeout 1000000 --no-lockfile --ignore-scripts prisma@6.11.1
RUN apk add --no-cache pnpm 7zip nginx
RUN pnpm install prisma@7.3.0
# init prisma to download all required files
RUN pnpm prisma init
COPY --from=build-system /app/prisma.config.ts ./
COPY --from=build-system /app/.output ./app
COPY --from=build-system /app/prisma ./prisma
COPY --from=build-system /app/build ./startup
COPY --from=build-system /app/build/nginx.conf /nginx.conf
COPY --from=torrential-build /build/target/release/torrential /usr/bin/
ENV LIBRARY="/library"
ENV DATA="/data"
ENV NGINX_CONFIG="/nginx.conf"
# Nuxt's port
ENV PORT=4000
CMD ["sh", "/app/startup/launch.sh"]
+1 -1
View File
@@ -1,3 +1,3 @@
# Server
The hosted, accessible portion of Drop. Exposes a web UI and API for applications to use.
The hosted, accessible portion of Drop. Exposes a web UI and API for applications to use.
+1 -1
View File
@@ -5,4 +5,4 @@ plugins:
opt: target=ts
inputs:
- directory: ../
- directory: ../
+2 -2
View File
@@ -19,7 +19,7 @@
</p>
<NuxtLink
class="mt-4 rounded-md inline-flex items-center text-sm font-semibold text-blue-500 hover:text-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
href="https://docs.droposs.org/docs/authentication/simple"
href="https://droposs.org/docs/admin/authentication/simple"
target="_blank"
>
<i18n-t
@@ -74,7 +74,7 @@
</p>
<NuxtLink
class="mt-4 rounded-md inline-flex items-center text-sm font-semibold text-blue-500 hover:text-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500"
href="https://docs.droposs.org/docs/authentication/oidc"
href="https://droposs.org/docs/admin/authentication/oidc"
target="_blank"
>
<i18n-t
+3 -2
View File
@@ -174,13 +174,14 @@ const optionsMetadata: {
Filesystem: {
title: t("library.admin.sources.fsTitle"),
description: t("library.admin.sources.fsDesc"),
docsLink: "https://docs.droposs.org/docs/library#drop-style",
docsLink: "https://droposs.org/docs/reference/library-sources#drop-style",
icon: DropLogo,
},
FlatFilesystem: {
title: t("library.admin.sources.fsFlatTitle"),
description: t("library.admin.sources.fsFlatDesc"),
docsLink: "https://docs.droposs.org/docs/library#flat-style-or-compat",
docsLink:
"https://droposs.org/docs/reference/library-sources#compatibility-flat-style",
icon: BackwardIcon,
},
};
+2 -2
View File
@@ -134,11 +134,11 @@ const navigation = computed(() => ({
// { name: t("footer.api"), href: "https://api.droposs.org/" },
{
name: t("footer.docs.server"),
href: "https://docs.droposs.org/docs/guides/quickstart",
href: "https://droposs.org/docs/admin/quickstart",
},
{
name: t("footer.docs.client"),
href: "https://docs.droposs.org/docs/guides/client",
href: "https://droposs.org/docs/user",
},
],
about: [
Submodule server/drop-base deleted from dad3487be6
+5
View File
@@ -11,7 +11,9 @@ export default withNuxt([
eslintConfigPrettier,
// vue-i18n plugin
// @ts-expect-error
...vueI18n.configs.recommended,
// @ts-expect-error
{
rules: {
// Optional.
@@ -34,6 +36,9 @@ export default withNuxt([
messageSyntaxVersion: "^11.0.0",
},
},
},
// @ts-expect-error
{
plugins: {
drop: { rules: { "no-prisma-delete": noPrismaDelete } },
},
+8 -9
View File
@@ -29,8 +29,8 @@
"@nuxt/image": "^1.10.0",
"@nuxt/kit": "^3.20.1",
"@nuxtjs/i18n": "^9.5.5",
"@prisma/adapter-pg": "^7.3.0",
"@prisma/client": "^7.3.0",
"@prisma/adapter-pg": "7.7.0",
"@prisma/client": "7.7.0",
"@simplewebauthn/browser": "^13.2.2",
"@simplewebauthn/server": "^13.2.2",
"@tailwindcss/vite": "^4.0.6",
@@ -52,13 +52,13 @@
"luxon": "^3.6.1",
"micromark": "^4.0.1",
"normalize-url": "^8.0.2",
"nuxt": "^3.20.1",
"nuxt": "^3.21.6",
"nuxt-security": "2.2.0",
"otp-io": "^1.2.7",
"parse-cosekey": "^1.0.2",
"pino": "^9.7.0",
"pino-pretty": "^13.0.0",
"prisma": "7.3.0",
"pino": "^9.14.0",
"pino-pretty": "^13.1.1",
"prisma": "7.7.0",
"sanitize-filename": "^1.6.3",
"semver": "^7.7.1",
"shescape": "^2.1.10",
@@ -90,7 +90,7 @@
"eslint-config-prettier": "^10.1.1",
"golar": "^0.0.13",
"h3": "^1.15.5",
"nitropack": "^2.11.12",
"nitropack": "^2.13.4",
"ofetch": "^1.4.1",
"prettier": "^3.5.3",
"prettier-plugin-sort-json": "^4.1.1",
@@ -103,6 +103,5 @@
"vue3-carousel-nuxt": {
"vue3-carousel": "^0.16.0"
}
},
"packageManager": "pnpm@10.29.1+sha512.48dae233635a645768a3028d19545cacc1688639eeb1f3734e42d6d6b971afbf22aa1ac9af52a173d9c3a20c15857cfa400f19994d79a2f626fcc73fccda9bbc"
}
}
+1 -1
View File
@@ -438,7 +438,7 @@
<NuxtLink
class="transition text-xs text-zinc-600 hover:underline hover:text-zinc-400"
href="https://docs.droposs.org/docs/library"
href="https://droposs.org/docs/reference/library-sources"
target="_blank"
>
<i18n-t
+3 -2
View File
@@ -279,13 +279,14 @@ const optionsMetadata: {
Filesystem: {
title: t("library.admin.sources.fsTitle"),
description: t("library.admin.sources.fsDesc"),
docsLink: "https://docs.droposs.org/docs/library#drop-style",
docsLink: "https://droposs.org/docs/reference/library-sources#drop-style",
icon: DropLogo,
},
FlatFilesystem: {
title: t("library.admin.sources.fsFlatTitle"),
description: t("library.admin.sources.fsFlatDesc"),
docsLink: "https://docs.droposs.org/docs/library#flat-style-or-compat",
docsLink:
"https://droposs.org/docs/reference/library-sources#compatibility-flat-style",
icon: BackwardIcon,
},
};
@@ -14,9 +14,9 @@ export const FilesystemProviderConfig = type({
baseDir: "string",
});
export class FilesystemProvider
implements LibraryProvider<typeof FilesystemProviderConfig.infer>
{
export class FilesystemProvider implements LibraryProvider<
typeof FilesystemProviderConfig.infer
> {
private config: typeof FilesystemProviderConfig.infer;
private myId: string;
@@ -11,9 +11,9 @@ export const FlatFilesystemProviderConfig = type({
baseDir: "string",
});
export class FlatFilesystemProvider
implements LibraryProvider<typeof FlatFilesystemProviderConfig.infer>
{
export class FlatFilesystemProvider implements LibraryProvider<
typeof FlatFilesystemProviderConfig.infer
> {
private config: typeof FlatFilesystemProviderConfig.infer;
private myId: string;
@@ -188,7 +188,10 @@ export class PCGamingWikiProvider implements MetadataProvider {
return url.pathname.replace("/games/", "").replace(/\/$/, "");
}
default: {
logger.warn("Pcgamingwiki, unknown host", url.hostname);
logger.warn(
{ hostname: url.hostname },
"Pcgamingwiki, unknown host",
);
return undefined;
}
}
@@ -222,8 +225,8 @@ export class PCGamingWikiProvider implements MetadataProvider {
});
if (ratingObj instanceof type.errors) {
logger.info(
{ summary: ratingObj.summary },
"pcgamingwiki: failed to properly get review rating",
ratingObj.summary,
);
return undefined;
}
@@ -327,7 +330,7 @@ export class PCGamingWikiProvider implements MetadataProvider {
* @returns
*/
private parseTS(isoStr: string): DateTime {
return DateTime.fromISO(isoStr.split(";")[0]);
return DateTime.fromISO(isoStr.split(";")[0]!);
}
private parseWebsitesGetFirst(websiteStr?: string | null): string {
@@ -429,7 +432,7 @@ export class PCGamingWikiProvider implements MetadataProvider {
);
const released = game.Released
? DateTime.fromISO(game.Released.split(";")[0]).toJSDate()
? DateTime.fromISO(game.Released.split(";")[0]!).toJSDate()
: new Date();
const metadata: GameMetadata = {
+13 -4
View File
@@ -306,7 +306,8 @@ export class SteamProvider implements MetadataProvider {
"https://store.steampowered.com/publisher/",
),
)
.map((v) => v.attribs.href);
.map((v) => v.attribs.href)
.filter((v) => v !== undefined);
const companies: {
[key: string]: {
@@ -320,6 +321,8 @@ export class SteamProvider implements MetadataProvider {
.substring("https://store.steampowered.com/".length, v.indexOf("?"))
.split("/");
if (!type || !name) return;
companies[name] ??= { pub: false, dev: false };
switch (type) {
case "publisher":
@@ -546,7 +549,9 @@ export class SteamProvider implements MetadataProvider {
let titleMatch = ogTitleRegex.exec(html);
titleMatch ??= titleTagRegex.exec(html);
return titleMatch ? this._decodeHtmlEntities(titleMatch[1]) : undefined;
return titleMatch && titleMatch[1]
? this._decodeHtmlEntities(titleMatch[1])
: undefined;
}
private _extractDescription(html: string): string | undefined {
@@ -558,7 +563,9 @@ export class SteamProvider implements MetadataProvider {
let descMatch = ogDescRegex.exec(html);
descMatch ??= nameDescRegex.exec(html);
return descMatch ? this._decodeHtmlEntities(descMatch[1]) : undefined;
return descMatch && descMatch[1]
? this._decodeHtmlEntities(descMatch[1])
: undefined;
}
private _extractImage(html: string): string | undefined {
@@ -583,6 +590,7 @@ export class SteamProvider implements MetadataProvider {
curatorUrlMatch ??= linkfilterRegex.exec(html);
if (!curatorUrlMatch) return undefined;
if (!curatorUrlMatch[1]) return undefined;
try {
return decodeURIComponent(curatorUrlMatch[1]);
@@ -601,11 +609,12 @@ export class SteamProvider implements MetadataProvider {
bannerMatch ??= backgroundImageRegex.exec(html);
if (!bannerMatch) return undefined;
if (!bannerMatch[1]) return undefined;
let bannerUrl = bannerMatch[1].replace(/['"]/g, "");
// Clean up the URL
if (bannerUrl.includes("?")) {
bannerUrl = bannerUrl.split("?")[0];
bannerUrl = bannerUrl.split("?")[0]!;
}
return bannerUrl;
}
+5 -2
View File
@@ -123,7 +123,10 @@ export class FsObjectBackend extends ObjectBackend {
const metadataRaw = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
const metadata = objectMetadata(metadataRaw);
if (metadata instanceof type.errors) {
logger.error("FsObjectBackend#fetchMetadata", metadata.summary);
logger.error(
{ summary: metadata.summary },
"FsObjectBackend#fetchMetadata",
);
return undefined;
}
await this.metadataCache.set(id, metadata);
@@ -198,8 +201,8 @@ export class FsObjectBackend extends ObjectBackend {
);
} catch (error) {
cleanupLogger.error(
{ error },
`[FsObjectBackend#cleanupMetadata]: Failed to remove ${file}`,
error,
);
}
}
+1 -1
View File
@@ -190,7 +190,7 @@ class TaskHandler {
parentTask?.progress ??
((progress: number) => {
if (progress < 0 || progress > 100) {
logger.error("Progress must be between 0 and 100", { progress });
logger.error({ progress }, "Progress must be between 0 and 100");
return;
}
const taskEntry = this.taskPool.get(task.id);
@@ -49,10 +49,13 @@ export default defineDropTask({
// if response failed somehow
if (!response.ok) {
logger.info("Failed to check for update ", {
status: response.status,
body: response.body,
});
logger.info(
{
status: response.status,
body: response.body,
},
"Failed to check for update ",
);
throw new Error(
`Failed to check for update: ${response.status} ${response.body}`,
+7 -6
View File
@@ -41,7 +41,7 @@ export default defineConfig({
{ slug: "user" },
{
label: "Install",
autogenerate: { directory: "user/install" },
items: [{ autogenerate: { directory: "user/install" } }],
},
{
label: "Usage",
@@ -65,25 +65,26 @@ export default defineConfig({
},
{
label: "Going further",
autogenerate: { directory: "admin/going-further" },
items: [{ autogenerate: { directory: "admin/going-further" } }],
},
{
label: "Metadata",
autogenerate: { directory: "admin/metadata" },
items: [{ autogenerate: { directory: "admin/metadata" } }],
},
{
label: "Authentication",
autogenerate: { directory: "admin/authentication" },
items: [{ autogenerate: { directory: "admin/authentication" } }],
},
],
},
{
label: "Reference",
autogenerate: { directory: "reference" },
items: [{ autogenerate: { directory: "reference" } }],
},
],
customCss: ["./src/styles/drop.css"],
}),
],
site: "https://docs-next.droposs.org/",
site: "https://droposs.org",
base: "/docs",
});
+5 -5
View File
@@ -10,11 +10,11 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.37.4",
"astro": "^5.6.1",
"sharp": "^0.34.2",
"starlight-image-zoom": "^0.13.2",
"starlight-links-validator": "^0.19.2",
"@astrojs/starlight": "^0.40.0",
"astro": "^6.4.8",
"sharp": "^0.35.2",
"starlight-image-zoom": "^0.14.2",
"starlight-links-validator": "^0.24.1",
"starlight-theme-rapide": "^0.5.2"
}
}
@@ -28,7 +28,7 @@ For convenience's sake, we can also specify file extensions for Drop's auto-dete
Add a launch executable for every platform you want to support. The options are fairly self-explanatory, but make sure to use the `{rom}` placeholder, and optionally add the file extensions.
Read the [Command Parsing](/reference/command-parsing/) article to understand how it's parsed and substituted.
Read the [Command Parsing](/docs/reference/command-parsing/) article to understand how it's parsed and substituted.
3. ## Import your game
@@ -18,7 +18,7 @@ If you're using a library source that supports versioning, you can add and impor
2. ### Follow the import guide again
Follow the [import guide again](/admin/guides/import-version/), but this time for your new version folder.
Follow the [import guide again](/docs/admin/guides/import-version/), but this time for your new version folder.
</Steps>
@@ -39,7 +39,7 @@ You can stack many "update mode" versions on top of each other, and they will pi
2. ### Follow the import guide again
Follow the [import guide again](/admin/guides/import-version/), but this time for your new version folder.
Follow the [import guide again](/docs/admin/guides/import-version/), but this time for your new version folder.
3. ### Before import, enable update mode
@@ -3,7 +3,7 @@ title: Setting up OIDC
---
:::note
You can find reference information in the [OIDC authentication docs](/admin/authentication/oidc/).
You can find reference information in the [OIDC authentication docs](/docs/admin/authentication/oidc/).
:::
## Authentik
@@ -10,7 +10,7 @@ To import games and start using Drop, you must first create a library to import
1. **Decide on a library layout.**
Drop supports different layouts for your files on disk, you can read more about them in the [Library Sources](/reference/library-sources) reference section.
Drop supports different layouts for your files on disk, you can read more about them in the [Library Sources](/docs/reference/library-sources) reference section.
2. **Mount your library in the Docker container.**
@@ -28,7 +28,7 @@ To import games and start using Drop, you must first create a library to import
- `/mnt/media/my-drop-library` is the path to your library.
- `/library` is a **unique** path inside the container. **Use something else if another volume mounts to `/library`**.
If you followed the [Quickstart](/admin/quickstart/) guide, you'll have already set up a library at `./library` pointing to `/library` within the container. You may want to instead edit that line in the `volumes` section to point to where your library is located.
If you followed the [Quickstart](/docs/admin/quickstart/) guide, you'll have already set up a library at `./library` pointing to `/library` within the container. You may want to instead edit that line in the `volumes` section to point to where your library is located.
3. **Open library source interface in Admin Dashboard.**
@@ -16,7 +16,7 @@ Drop automatically parses and formats the URL, so there are no requirements on t
## LAN
The `compose.yaml` provided in the [Quickstart guide](/admin/quickstart/) already exposes the Drop instance on port 3000. If you're on the same LAN as your Drop instance, you can find it's IP and then use:
The `compose.yaml` provided in the [Quickstart guide](/docs/admin/quickstart/) already exposes the Drop instance on port 3000. If you're on the same LAN as your Drop instance, you can find it's IP and then use:
```
http://[instance IP]:3000
@@ -60,7 +60,7 @@ Once you've got a library set up, and have imported a game, you can import a ver
A installer version uses "setup mode". Enable the option, and then add the installer executable in setup commands.
:::note
Setup and launch commands are parsed in a cross-platform, POSIX style. It's not relevant for simple setups, but useful to know. Read more about it in [Command Parsing](/reference/command-parsing/).
Setup and launch commands are parsed in a cross-platform, POSIX style. It's not relevant for simple setups, but useful to know. Read more about it in [Command Parsing](/docs/reference/command-parsing/).
:::
6. ### **Wait for import.**
@@ -40,7 +40,7 @@ services:
**The main things in this `compose.yaml` is the volumes attached to the `drop` service:**
1. `./library` is where you will put your games to be imported into Drop. See '[Creating a library](/admin/guides/creating-library/)' once you're set up.
1. `./library` is where you will put your games to be imported into Drop. See '[Creating a library](/docs/admin/guides/creating-library/)' once you're set up.
2. `./data` is where Drop will store anything that's using the default file-system backed storage system. Typically, these are objects.
:::tip
+1 -1
View File
@@ -7,7 +7,7 @@ hero:
file: ../../assets/drop.svg
actions:
- text: Quickstart
link: /admin/quickstart
link: /docs/admin/quickstart
icon: right-arrow
- text: Download client
link: https://droposs.org/download
@@ -89,7 +89,7 @@ Drop's [dockerfile](https://github.com/Drop-OSS/drop/blob/develop/Dockerfile) pr
:::
```bash
npm install prisma@7.3.0 dotenv # dotenv is required
npm install prisma@7.7.0 dotenv # dotenv is required
```
Then, with your database running:
@@ -21,12 +21,40 @@ Then, what happens with this, depends on the type of game we're launching:
## Normal (no emulator)
Drop reconstructs the original shell string, and passes it into platform-specific command wrappers. For Windows, this means nothing. For Linux, it gets wrapped in `umu-run`.
Drop reconstructs the original shell string, and passes it into a platform-specific command wrapper, called a **launch method**. Drop picks a sensible launch method automatically, but you can override it per-game for troubleshooting — see [Launch methods](#launch-methods) below.
By default, on Windows the command is launched based on its file type: `.exe` files run directly, `.bat` and `.cmd` files run through `cmd`, `.ps1` files run through PowerShell, and anything else is handed to `cmd` so builtins, `PATHEXT` resolution and `%VAR%` expansion all work. On Linux, native games run directly on the host, while games targeting Windows are wrapped in `umu-run` (with Proton).
It is then parsed again, and then passed into process creation, mapping the environment variable, command, and arguments into their respective platform-dependent places.
Drop logs out it's final parsed command, if you want to look at it in the client logs.
## Launch methods
The wrapper Drop uses to start a game is called a **launch method** (a *process handler* internally). Drop automatically selects the best available method for each game, but if a game won't launch you can override it under **Game Options → Launch → Launch method**.
Only methods supported by your current platform (and the game's target platform) are listed, each with a short description in the client.
### Windows
| Method | Description |
| ------ | ----------- |
| **Automatic** *(default)* | Detects the file type and launches it directly, or through `cmd` or PowerShell. |
| **Direct executable** | Runs the executable directly, without a shell. |
| **Command Prompt (cmd)** | Launches through `cmd.exe`. Supports batch files, builtins and `%VAR%` expansion. |
| **PowerShell** | Runs the command as a PowerShell script (`-File`). |
### Linux
| Method | Description |
| ------ | ----------- |
| **Native (direct)** *(default for Linux games)* | Runs the native Linux game directly on the host. |
| **Steam Linux Runtime (umu-run)** | Runs the native Linux game inside `umu-run`'s Steam Linux Runtime. Requires [UMU launcher](/docs/user/usage/proton/). |
| **Proton (umu-run)** *(default for Windows games)* | Runs a Windows game through Proton, using `umu-run`. Requires [Proton](/docs/user/usage/proton/). |
| **Proton + muvm (Asahi)** | Runs a Windows game through Proton inside a muvm microVM, for Apple Silicon / Asahi Linux. |
On macOS, games are always launched directly.
## Emulators
For emulators, we have the "emulator version" (version containing the emulator), and the "emulated version" (version containing the ROM).
@@ -48,7 +48,7 @@ In the UI, you'll be prompted to "import" each folder separately:
So your game has gotten an update and you've got new files. All you need to do is create a new version folder inside the game folder, and move all the files you have into that folder. Then, import it within the Drop admin UI.
If you have files that you're supposed to **paste over the previous version**, Drop supports that! Read [Update mode](/reference/update-mode/) to find out more.
If you have files that you're supposed to **paste over the previous version**, Drop supports that! Read [Update mode](/docs/reference/update-mode/) to find out more.
# Compatibility (flat-style)
+1 -1
View File
@@ -4,4 +4,4 @@ title: Getting Started
Drop clients are available for download from [our website](https://droposs.org/download), or follow one of our installation guides on the sidebar. Download the correct version for your platform, and open it up.
The client will walk you through the setup and sign-in process to get started. You'll need a Drop instance you can connect to, and an account on the server. If you don't have one, you can follow the [Quickstart](/admin/quickstart/) guide to set up your own.
The client will walk you through the setup and sign-in process to get started. You'll need a Drop instance you can connect to, and an account on the server. If you don't have one, you can follow the [Quickstart](/docs/admin/quickstart/) guide to set up your own.
@@ -55,4 +55,8 @@ To launch any Windows game, you **must** first set a default Proton version.
Drop uses a global default Proton version to launch games by default. You can override this in a game's options.
![Screenshot showing how to override the proton version](./proton-options-override.png)
![Screenshot showing how to override the proton version](./proton-options-override.png)
## Choosing a launch method
Proton isn't the only thing you can change per-game. If a game won't start, you can also try a different **launch method** from the same **Game Options → Launch** menu — for example, forcing a Windows game through Proton, or running a native Linux game inside the Steam Linux Runtime. See [Launch methods](/docs/reference/command-parsing/#launch-methods) for the full list.
-80
View File
@@ -1,80 +0,0 @@
name: Deploy Next.js site to Pages
on:
# Runs on pushes targeting the main branch
push:
branches:
- main
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 10
run_install: false
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Setup Pages
id: setup_pages
uses: actions/configure-pages@v5
- name: Restore cache
uses: actions/cache@v4
with:
path: |
.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-
- name: Build with Next.js
run: pnpm run build
env:
PAGES_BASE_PATH: ${{ steps.setup_pages.outputs.base_path }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./out
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+1 -1
View File
@@ -17,7 +17,7 @@
"dayjs": "^1.11.13",
"feed": "^5.1.0",
"framer-motion": "^12.23.11",
"next": "15.4.4",
"next": "15.5.18",
"react": "^19",
"react-dom": "^19",
"react-use-measure": "^2.1.7",
+2 -2
View File
@@ -46,7 +46,7 @@ function Header() {
<div className="flex flex-col gap-y-2 border-b border-dotted border-zinc-800 pb-4">
<dt className="text-sm/6 text-zinc-400">Lines of code</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={10} end={40} />k
<AnimatedNumber start={0} end={75} />k
</dd>
</div>
<div className="flex flex-col gap-y-2 border-b border-dotted border-zinc-800 pb-4">
@@ -61,7 +61,7 @@ function Header() {
<div className="flex flex-col gap-y-2 max-sm:border-b max-sm:border-dotted max-sm:border-gray-200 max-sm:pb-4">
<dt className="text-sm/6 text-zinc-400">Docker pulls</dt>
<dd className="order-first text-6xl font-medium tracking-tight">
<AnimatedNumber start={0} end={48.8} decimals={1} />k
<AnimatedNumber start={0} end={210} decimals={1} />k
</dd>
</div>
<div className="flex flex-col gap-y-2">
+81 -7
View File
@@ -6,7 +6,14 @@ import { Gradient } from '@/components/gradient'
import { LogoCluster } from '@/components/logo-cluster'
import { Navbar } from '@/components/navbar'
import { Heading, Subheading } from '@/components/text'
import { ArrowDownCircleIcon } from '@heroicons/react/24/solid'
import {
BuildingStorefrontIcon,
CloudArrowDownIcon,
ComputerDesktopIcon,
PencilSquareIcon,
ServerStackIcon,
ShieldCheckIcon,
} from '@heroicons/react/24/solid'
import type { Metadata } from 'next'
export const metadata: Metadata = {
@@ -29,7 +36,7 @@ function Hero() {
Steam and Epic.
</p>
<div className="mt-12 flex flex-col gap-x-6 gap-y-4 sm:flex-row">
<Button href="https://docs.droposs.org/docs/guides/quickstart">
<Button href="/docs/admin/quickstart">
Get started
</Button>
<Button variant="outline" href="/about">
@@ -54,7 +61,7 @@ function FeatureSection() {
<p className="mt-6 text-lg/8 text-zinc-400">
Drop is built from the ground up to be flexible, fast, and
beautiful. It's designed to scale with your library, and handle
beautiful. It&apos;s designed to scale with your library, and handle
thousands of games.
</p>
</div>
@@ -77,13 +84,80 @@ function FeatureSection() {
<dl className="mx-auto grid max-w-2xl grid-cols-1 gap-x-6 gap-y-10 text-base/7 text-zinc-400 sm:grid-cols-2 lg:mx-0 lg:max-w-none lg:grid-cols-3 lg:gap-x-8 lg:gap-y-16">
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<ArrowDownCircleIcon
<ServerStackIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
ADASDASD
Self-hosted &amp; open-source.
</dt>{' '}
<dd className="inline">ASDASDASDAS</dd>
<dd className="inline">
Run Drop entirely on your own hardware. Your library, your data,
your rules &mdash; all under the AGPLv3.
</dd>
</div>
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<PencilSquareIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
Rich metadata editing.
</dt>{' '}
<dd className="inline">
Customise names, descriptions, and icons with full Markdown and
image support.
</dd>
</div>
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<CloudArrowDownIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
Automatic imports.
</dt>{' '}
<dd className="inline">
Pull cover art and game details straight from IGDB, GiantBomb, and
PCGamingWiki.
</dd>
</div>
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<BuildingStorefrontIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
A built-in store.
</dt>{' '}
<dd className="inline">
Let users browse, filter, and collect games through a fully
featured store.
</dd>
</div>
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<ShieldCheckIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
Flexible authentication.
</dt>{' '}
<dd className="inline">
Use simple accounts or hook into your existing SSO.
</dd>
</div>
<div className="relative pl-9">
<dt className="inline font-semibold text-zinc-100">
<ComputerDesktopIcon
aria-hidden="true"
className="absolute top-1 left-1 size-5 text-blue-600"
/>
Native desktop client.
</dt>{' '}
<dd className="inline">
Download, install, and play your whole library through a
cross-platform desktop client.
</dd>
</div>
</dl>
</div>
@@ -127,7 +201,7 @@ function BentoSection() {
<BentoCard
eyebrow="Authentication"
title="Flexible authentication"
description="Drop supports both simple and SSO authentication, with more features like SCIM on the way."
description="Drop supports both simple and SSO authentication, so users can sign in however suits them."
graphic={
<div className="flex h-full w-full items-center justify-center p-4">
<div className="bg-position-center h-full w-full grow rounded-lg bg-[url(/screenshots/authentication.png)] bg-cover bg-no-repeat" />
@@ -35,6 +35,12 @@ export function BentoCard({
>
<div className="relative h-80 shrink-0">
{graphic}
{fade.includes('top') && (
<div className="absolute inset-0 bg-linear-to-b from-zinc-900 to-50%" />
)}
{fade.includes('bottom') && (
<div className="absolute inset-0 bg-linear-to-t from-zinc-900 to-50%" />
)}
</div>
<div className="relative p-10">
<Subheading as="h3" dark={dark}>
+2 -2
View File
@@ -23,7 +23,7 @@ function CallToAction() {
<div className="mt-6">
<Button
className="w-full sm:w-auto"
href="https://docs.droposs.org/docs/guides/quickstart"
href="/docs/admin/quickstart"
>
Quickstart &rarr;
</Button>
@@ -65,7 +65,7 @@ function Sitemap() {
<div>
<SitemapHeading>Documentation</SitemapHeading>
<SitemapLinks>
<SitemapLink href="https://docs.droposs.org/">
<SitemapLink href="/docs">
Self-hosters
</SitemapLink>
<SitemapLink href="https://developer.droposs.org/">
+4 -4
View File
@@ -80,11 +80,11 @@ export function Gallery() {
)
.map((file) => (
<div key={file.url} className="relative w-full">
<div className="group overflow-hidden rounded-lg bg-gray-100 focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-blue-600">
<div className="group relative block w-full overflow-hidden rounded-lg bg-gray-100 focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-blue-600">
<img
alt=""
src={file.url}
className="pointer-events-none aspect-10/7 aspect-auto rounded-lg object-cover outline -outline-offset-1 outline-black/5 group-hover:opacity-75"
className="pointer-events-none block w-full rounded-lg object-cover outline -outline-offset-1 outline-black/5 group-hover:opacity-75"
/>
<button
type="button"
@@ -96,10 +96,10 @@ export function Gallery() {
</span>
</button>
</div>
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-gray-900">
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-zinc-100">
{file.name}
</p>
<p className="pointer-events-none block text-xs font-medium text-gray-500">
<p className="pointer-events-none block text-xs font-medium text-zinc-400">
{file.description}
</p>
</div>
+1 -1
View File
@@ -46,7 +46,7 @@ function Circles() {
<Circle size={400} opacity="5%" delay={0.3} />
<Circle size={272} opacity="5%" delay={0.15} />
<Circle size={144} opacity="10%" delay={0} />
<div className="absolute inset-0 bg-linear-to-t from-white to-35%" />
<div className="absolute inset-0 bg-linear-to-t from-zinc-900 to-35%" />
</div>
)
}
+1
View File
@@ -79,6 +79,7 @@ function SponsorCard({
className="relative flex w-64 rounded-3xl sm:w-72 bg-black"
>
<figure className="relative p-10">
<img alt={name} src={img} className="mb-4 size-12 rounded-full" />
<figcaption className="pb-3 border-b border-white/20">
<p className="text-sm/6 font-medium text-white">{name}</p>
<p className="text-sm/6 font-medium">
+13 -13
View File
@@ -271,9 +271,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytes"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cast"
@@ -1299,9 +1299,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.1.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-integer"
@@ -1634,9 +1634,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.13"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
@@ -2240,30 +2240,30 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.44"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.24"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",