Compare commits

...

4 Commits

Author SHA1 Message Date
DecDuck
3f85d21f97 fix: style fixes and server error messages 2025-11-30 23:12:16 +11:00
DecDuck
28d7a741c1 Move to pnpm to fix builds 2025-11-19 23:02:20 +11:00
DecDuck
490a689497 Fix Apple signing 2025-11-19 22:14:57 +11:00
DecDuck
84f4210479 Bump version 2025-11-15 09:09:58 +11:00
24 changed files with 14006 additions and 13589 deletions

View File

@@ -1,4 +1,4 @@
name: 'publish'
name: "publish"
on:
workflow_dispatch: {}
@@ -18,16 +18,16 @@ jobs:
fail-fast: false
matrix:
include:
- platform: 'macos-latest' # for Arm based macs (M1 and above).
args: '--target aarch64-apple-darwin'
- platform: 'macos-latest' # for Intel based macs.
args: '--target x86_64-apple-darwin'
- platform: 'ubuntu-22.04' # for Tauri v1 you could replace this with ubuntu-20.04.
args: ''
- platform: 'ubuntu-22.04-arm'
args: '--target aarch64-unknown-linux-gnu'
- platform: 'windows-latest'
args: ''
- platform: "macos-14" # for Arm based macs (M1 and above).
args: "--target aarch64-apple-darwin"
- platform: "macos-14" # for Intel based macs.
args: "--target x86_64-apple-darwin"
- platform: "ubuntu-22.04" # for Tauri v1 you could replace this with ubuntu-20.04.
args: ""
- platform: "ubuntu-22.04-arm"
args: "--target aarch64-unknown-linux-gnu"
- platform: "windows-latest"
args: ""
runs-on: ${{ matrix.platform }}
steps:
@@ -36,16 +36,29 @@ jobs:
submodules: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: pnpm
- name: install Rust nightly
uses: dtolnay/rust-toolchain@nightly
with:
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
targets: ${{ matrix.platform == 'macos-14' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: './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.
@@ -54,9 +67,8 @@ jobs:
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
# webkitgtk 4.0 is for Tauri v1 - webkitgtk 4.1 is for Tauri v2.
- name: Import Apple Developer Certificate
if: matrix.platform == 'macos-latest'
if: matrix.platform == 'macos-14'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -67,18 +79,30 @@ jobs:
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
curl https://droposs.org/drop.crt --output drop.pem
sudo security authorizationdb write com.apple.trust-settings.user allow
security add-trusted-cert -r trustRoot -k build.keychain -p codeSign -u -1 drop.pem
sudo security authorizationdb remove com.apple.trust-settings.user
echo "Created keychain"
curl https://droposs.org/drop.der --output drop.der
# swiftc libs/appletrust/add-certificate.swift
# ./add-certificate drop.der
# rm add-certificate
# echo "Added certificate to keychain using swift util"
## Script is equivalent to:
sudo security authorizationdb write com.apple.trust-settings.admin allow
sudo security add-trusted-cert -d -r trustRoot -k build.keychain -p codeSign -u -1 drop.der
sudo security authorizationdb remove com.apple.trust-settings.admin
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
echo "Imported certificate"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security find-identity -v -p codesigning build.keychain
- name: Verify Certificate
if: matrix.platform == 'macos-latest'
if: matrix.platform == 'macos-14'
run: |
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Drop OSS")
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
@@ -86,7 +110,7 @@ jobs:
echo "Certificate imported. Using identity: $CERT_ID"
- name: install frontend dependencies
run: yarn install # change this to npm, pnpm or bun depending on which one you use.
run: pnpm install # change this to npm, pnpm or bun depending on which one you use.
- uses: tauri-apps/tauri-action@v0
env:
@@ -97,8 +121,8 @@ jobs:
NO_STRIP: true
with:
tagName: 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.'
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 }}
args: ${{ matrix.args }}

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -40,10 +40,10 @@ for (const view of views) {
process.chdir(`./${view}`);
loggerChild.info(`Install deps for "${view}"`);
await spawn("yarn");
await spawn("pnpm install");
loggerChild.info(`Building "${view}"`);
await spawn("yarn build", {
await spawn("pnpm run build", {
env: { ...process.env, NUXT_APP_BASE_URL: `/${view}/` },
});

View File

@@ -0,0 +1,72 @@
import Foundation
import Security
enum SecurityError: Error {
case generalError
}
func deleteCertificateFromKeyChain(_ certificateLabel: String) -> Bool {
let delQuery: [NSString: Any] = [
kSecClass: kSecClassCertificate,
kSecAttrLabel: certificateLabel,
]
let delStatus: OSStatus = SecItemDelete(delQuery as CFDictionary)
return delStatus == errSecSuccess
}
func saveCertificateToKeyChain(_ certificate: SecCertificate, certificateLabel: String) throws {
SecKeychainSetPreferenceDomain(SecPreferencesDomain.system)
deleteCertificateFromKeyChain(certificateLabel)
let setQuery: [NSString: AnyObject] = [
kSecClass: kSecClassCertificate,
kSecValueRef: certificate,
kSecAttrLabel: certificateLabel as AnyObject,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked,
kSecAttrCanSign: true as AnyObject,
]
let addStatus: OSStatus = SecItemAdd(setQuery as CFDictionary, nil)
guard addStatus == errSecSuccess else {
throw SecurityError.generalError
}
var status = SecTrustSettingsSetTrustSettings(certificate, SecTrustSettingsDomain.admin, nil)
}
func getCertificateFromString(stringData: String) throws -> SecCertificate {
if let data = NSData(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) {
if let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, data) {
return certificate
}
}
throw SecurityError.generalError
}
if CommandLine.arguments.count != 2 {
print("Usage: \(CommandLine.arguments[0]) [cert.file]")
print("Usage: \(CommandLine.arguments[0]) --version")
exit(1)
}
if (CommandLine.arguments[1] == "--version") {
let version = "dev"
print(version)
exit(0)
} else {
let fileURL = URL(fileURLWithPath: CommandLine.arguments[1])
do {
let certData = try Data(contentsOf: fileURL)
let certificate = SecCertificateCreateWithData(nil, certData as CFData)
if certificate != nil {
try? saveCertificateToKeyChain(certificate!, certificateLabel: "DropOSS")
exit(0)
} else {
print("ERROR: Unknown error while reading the \(CommandLine.arguments[1]) file.")
}
} catch {
print("ERROR: Unexpected error while reading the \(CommandLine.arguments[1]) file. \(error)")
}
}
exit(1)

View File

@@ -73,7 +73,7 @@
alt=""
/>
</div>
<div class="inline-flex items-center gap-x-2">
<div class="truncate inline-flex items-center gap-x-2">
<p
class="text-sm whitespace-nowrap font-display font-semibold"
>

View File

@@ -12,6 +12,7 @@ export default defineNuxtConfig({
css: ["~/assets/main.scss"],
ssr: false,
devtools: false,
extends: [["../libs/drop-base"]],

View File

@@ -1,7 +1,7 @@
{
"name": "view",
"private": true,
"version": "0.3.3",
"version": "0.3.4",
"type": "module",
"scripts": {
"build": "nuxt generate",
@@ -14,6 +14,8 @@
"@heroicons/vue": "^2.1.5",
"@nuxtjs/tailwindcss": "^6.12.2",
"@tauri-apps/api": "^2.7.0",
"@tauri-apps/plugin-os": "^2.3.2",
"@tauri-apps/plugin-shell": "^2.3.3",
"koa": "^2.16.1",
"markdown-it": "^14.1.0",
"micromark": "^4.0.1",
@@ -32,6 +34,5 @@
"tailwindcss": "^3.4.13",
"typescript": "^5.8.3",
"vue-tsc": "^2.2.10"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
}

8217
main/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

3
main/pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,3 @@
onlyBuiltDependencies:
- '@parcel/watcher'
- esbuild

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
WEBKIT_DISABLE_DMABUF_RENDERER=1 yarn tauri dev
WEBKIT_DISABLE_DMABUF_RENDERER=1 pnpm tauri dev

View File

@@ -3,7 +3,7 @@
ARCH=$(uname -m)
# build tauri apps
# NO_STRIP=true yarn tauri build -- --verbose
# NO_STRIP=true pnpm tauri build -- --verbose
# unpack appimage
APPIMAGE=$(ls ./src-tauri/target/release/bundle/appimage/*.AppImage)

View File

@@ -7,17 +7,11 @@
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^2.7.0",
"@tauri-apps/plugin-deep-link": "^2.4.1",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-opener": "^2.5.0",
"@tauri-apps/plugin-os": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.3.0",
"pino": "^9.7.0",
"pino-pretty": "^13.1.1",
"tauri": "^0.15.0"
},
"devDependencies": {
"@tauri-apps/cli": "^2.7.1"
"@tauri-apps/cli": "^2.9.4"
}
}

5583
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

21
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,21 @@
onlyBuiltDependencies:
- sharp
overrides:
cross-spawn@<6.0.6: '>=6.0.6'
cross-spawn@>=7.0.0 <7.0.5: '>=7.0.5'
form-data@<2.5.4: '>=2.5.4'
got@<11.8.5: '>=11.8.5'
http-cache-semantics@<4.1.1: '>=4.1.1'
lodash@<4.17.21: '>=4.17.21'
lodash@>=4.0.0 <4.17.21: '>=4.17.21'
minimist@>=1.0.0 <1.2.6: '>=1.2.6'
nth-check@<2.0.1: '>=2.0.1'
semver-regex@<3.1.3: '>=3.1.3'
semver-regex@<3.1.4: '>=3.1.4'
semver@>=7.0.0 <7.5.2: '>=7.5.2'
sharp@<0.30.5: '>=0.30.5'
sharp@<0.32.6: '>=0.32.6'
tmp@<=0.2.3: '>=0.2.4'
tough-cookie@<4.1.3: '>=4.1.3'
trim-newlines@<3.0.1: '>=3.0.1'

35
src-tauri/Cargo.lock generated
View File

@@ -1339,7 +1339,7 @@ dependencies = [
[[package]]
name = "drop-app"
version = "0.3.3"
version = "0.3.4"
dependencies = [
"atomic-instant-full",
"bitcode",
@@ -5763,9 +5763,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.34.4"
version = "0.34.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6121216ff67fe4bcfe64508ea1700bc15f74937d835a07b4a209cc00a8926a84"
checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
dependencies = [
"bitflags 2.9.4",
"block2 0.6.2",
@@ -5840,9 +5840,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "2.8.5"
version = "2.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8"
dependencies = [
"anyhow",
"bytes",
@@ -5884,7 +5884,6 @@ dependencies = [
"tokio",
"tray-icon",
"url",
"urlpattern",
"webkit2gtk",
"webview2-com",
"window-vibrancy",
@@ -5893,9 +5892,9 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.4.1"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f"
checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d"
dependencies = [
"anyhow",
"cargo_toml",
@@ -5915,9 +5914,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.4.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f"
dependencies = [
"base64 0.22.1",
"brotli",
@@ -5942,9 +5941,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.4.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e"
checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -6125,9 +6124,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.8.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
checksum = "9368f09358496f2229313fccb37682ad116b7f46fa76981efe116994a0628926"
dependencies = [
"cookie",
"dpi",
@@ -6150,9 +6149,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.8.1"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807"
checksum = "929f5df216f5c02a9e894554401bcdab6eec3e39ec6a4a7731c7067fc8688a93"
dependencies = [
"gtk",
"http 1.3.1",
@@ -6177,9 +6176,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.7.0"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
checksum = "f6b8bbe426abdbf52d050e52ed693130dbd68375b9ad82a3fb17efb4c8d85673"
dependencies = [
"anyhow",
"brotli",

View File

@@ -1,6 +1,6 @@
[package]
name = "drop-app"
version = "0.3.3"
version = "0.3.4"
description = "The client application for the open-source, self-hosted game distribution platform Drop"
authors = ["Drop OSS"]
edition = "2024"
@@ -93,7 +93,7 @@ version = "0.1.5"
features = ["curly"]
[dependencies.tauri]
version = "2.7.0"
version = "2.9.3"
features = ["protocol-asset", "tray-icon"]
[dependencies.tokio]

View File

@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
pub enum Platform {
Windows,
Linux,
#[allow(non_camel_case_types)]
macOS,
}

View File

@@ -39,7 +39,8 @@ impl DropWriter<File> {
.write(true)
.create(true)
.truncate(false)
.open(&path)?;
.open(&path)
.inspect_err(|_v| warn!("failed to open {}", path.display()))?;
Ok(Self {
destination: BufWriter::with_capacity(1024 * 1024, destination),
hasher: Context::new(),
@@ -122,7 +123,7 @@ impl<'a> DropDownloadPipeline<'a, Response, File> {
.source
.read(&mut copy_buffer[0..size])
.inspect_err(|_| {
info!("got error from {}", drop.filename);
warn!("got error from {}", drop.filename);
})?;
remaining -= size;
last_bump += size;
@@ -230,7 +231,8 @@ pub fn download_game_bucket(
return Err(ApplicationDownloadError::DownloadError(
RemoteAccessError::InvalidResponse(DropServerError {
status_code: 400,
status_message: format!(
status_message: "Server Error".to_owned(),
message: format!(
"invalid number of Content-Lengths recieved: {i}, {lengths}"
),
}),
@@ -244,7 +246,8 @@ pub fn download_game_bucket(
return Err(ApplicationDownloadError::DownloadError(
RemoteAccessError::InvalidResponse(DropServerError {
status_code: 400,
status_message: format!(
status_message: "Server Error".to_owned(),
message: format!(
"for {}, expected {}, got {} ({})",
drop.filename, drop.length, raw_length, length
),
@@ -272,7 +275,12 @@ pub fn download_game_bucket(
#[cfg(unix)]
{
for drop in bucket.drops.iter() {
let permissions = Permissions::from_mode(drop.permissions);
let permission = if drop.permissions == 0 {
0o744
} else {
drop.permissions
};
let permissions = Permissions::from_mode(permission);
set_permissions(drop.path.clone(), permissions)
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
}

View File

@@ -80,7 +80,7 @@ pub async fn fetch_user() -> Result<User, RemoteAccessError> {
let err: DropServerError = response.json().await?;
warn!("{err:?}");
if err.status_message == "Nonce expired" {
if err.message == "Nonce expired" {
return Err(RemoteAccessError::OutOfSync);
}
@@ -106,8 +106,8 @@ pub fn auth_initiate_logic(mode: String) -> Result<String, RemoteAccessError> {
name: format!("{} (Desktop)", hostname.display()),
platform: env::consts::OS.to_string(),
capabilities: HashMap::from([
("peerAPI".to_owned(), CapabilityConfiguration {}),
("cloudSaves".to_owned(), CapabilityConfiguration {}),
("PeerAPI".to_owned(), CapabilityConfiguration {}),
("CloudSaves".to_owned(), CapabilityConfiguration {}),
]),
mode,
};
@@ -117,9 +117,9 @@ pub fn auth_initiate_logic(mode: String) -> Result<String, RemoteAccessError> {
if response.status() != 200 {
let data: DropServerError = response.json()?;
error!("could not start handshake: {}", data.status_message);
error!("could not start handshake: {:?}", data);
return Err(RemoteAccessError::HandshakeFailed(data.status_message));
return Err(RemoteAccessError::HandshakeFailed(data.message));
}
let response = response.text()?;

View File

@@ -15,7 +15,7 @@ use serde::Deserialize;
pub struct DropServerError {
pub status_code: usize,
pub status_message: String,
// pub message: String,
pub message: String,
// pub url: String,
}
@@ -76,7 +76,7 @@ impl Display for RemoteAccessError {
RemoteAccessError::InvalidResponse(error) => write!(
f,
"server returned an invalid response: {}, {}",
error.status_code, error.status_message
error.status_code, error.message
),
RemoteAccessError::UnparseableResponse(error) => {
write!(f, "server returned an invalid response: {error}")

View File

@@ -6,7 +6,7 @@ use games::{
library::{FetchGameStruct, FrontendGameOptions, Game, get_current_meta, uninstall_game_logic},
state::{GameStatusManager, GameStatusWithTransient},
};
use log::warn;
use log::{info, warn};
use process::PROCESS_MANAGER;
use remote::{
auth::generate_authorization_header,
@@ -55,7 +55,8 @@ pub async fn fetch_library_logic(
if response.status() != 200 {
let err = response.json().await.unwrap_or(DropServerError {
status_code: 500,
status_message: "Invalid response from server.".to_owned(),
status_message: "Server Error".to_owned(),
message: "Invalid response from server.".to_owned(),
});
warn!("{err:?}");
return Err(RemoteAccessError::InvalidResponse(err));
@@ -223,6 +224,11 @@ pub async fn fetch_game_version_options_logic(
return Err(RemoteAccessError::InvalidResponse(err));
}
let raw = response.text().await?;
info!("{}", raw);
return Err(RemoteAccessError::CorruptedState);
let data: Vec<GameVersion> = response.json().await?;
let state_lock = state.lock();

View File

@@ -1,12 +1,12 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0",
"productName": "Drop Desktop Client",
"version": "0.3.3",
"version": "0.3.4",
"identifier": "dev.drop.client",
"build": {
"beforeDevCommand": "yarn --cwd main dev --port 1432",
"beforeDevCommand": "pnpm run -C main dev --port 1432",
"devUrl": "http://localhost:1432/",
"beforeBuildCommand": "yarn build",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../.output"
},
"app": {

5420
yarn.lock

File diff suppressed because it is too large Load Diff