Skip to content

Commit c6d8e70

Browse files
committed
fix: Resolve all compilation errors and consolidate workflows
- Fix ApiClient usage in auth modules (use reqwest directly) - Fix Config parameter passing (not String) - Fix borrow checker issues with refresh tokens - Consolidate Docker publishing into release workflow only - Remove redundant docker.yml workflow - Docker images now only published for stable releases
1 parent 4430b75 commit c6d8e70

File tree

5 files changed

+94
-88
lines changed

5 files changed

+94
-88
lines changed

.github/workflows/docker.yml

Lines changed: 0 additions & 63 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,53 @@ jobs:
205205
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
206206
continue-on-error: true # Don't fail if npm publish fails
207207

208+
publish-docker:
209+
name: Publish Docker Image
210+
needs: [create-release]
211+
runs-on: ubuntu-latest
212+
permissions:
213+
contents: read
214+
packages: write
215+
steps:
216+
- name: Checkout code
217+
uses: actions/checkout@v4
218+
219+
- name: Set up QEMU
220+
uses: docker/setup-qemu-action@v3
221+
with:
222+
platforms: linux/amd64,linux/arm64
223+
224+
- name: Set up Docker Buildx
225+
uses: docker/setup-buildx-action@v3
226+
227+
- name: Log in to Docker Hub
228+
uses: docker/login-action@v3
229+
with:
230+
username: ${{ secrets.DOCKERHUB_USERNAME }}
231+
password: ${{ secrets.DOCKERHUB_TOKEN }}
232+
233+
- name: Extract metadata
234+
id: meta
235+
uses: docker/metadata-action@v5
236+
with:
237+
images: vlawyer/kanuni
238+
tags: |
239+
type=semver,pattern={{version}},value=${{ needs.create-release.outputs.version }}
240+
type=semver,pattern={{major}}.{{minor}},value=${{ needs.create-release.outputs.version }}
241+
type=semver,pattern={{major}},value=${{ needs.create-release.outputs.version }}
242+
type=raw,value=latest
243+
244+
- name: Build and push Docker image
245+
uses: docker/build-push-action@v5
246+
with:
247+
context: .
248+
platforms: linux/amd64,linux/arm64
249+
push: true
250+
tags: ${{ steps.meta.outputs.tags }}
251+
labels: ${{ steps.meta.outputs.labels }}
252+
cache-from: type=gha
253+
cache-to: type=gha,mode=max
254+
208255
update-homebrew:
209256
name: Update Homebrew Formula
210257
needs: [create-release, build-release]

src/auth/api_key.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize};
99
use uuid::Uuid;
1010

1111
use super::token_store::{AuthType, StoredCredentials, TokenStore};
12-
use crate::api::ApiClient;
12+
use crate::config::Config;
13+
use reqwest::{Client, StatusCode};
1314

1415
#[derive(Debug, Serialize)]
1516
pub struct CreateApiKeyRequest {
@@ -43,14 +44,20 @@ pub struct ApiKeyInfo {
4344
}
4445

4546
pub struct ApiKeyManager {
46-
client: ApiClient,
47+
client: Client,
48+
base_url: String,
4749
store: TokenStore,
4850
}
4951

5052
impl ApiKeyManager {
51-
pub fn new(api_endpoint: String) -> Result<Self> {
53+
pub fn new(config: Config) -> Result<Self> {
54+
let client = Client::builder()
55+
.timeout(std::time::Duration::from_secs(30))
56+
.build()?;
57+
5258
Ok(Self {
53-
client: ApiClient::new(api_endpoint),
59+
client,
60+
base_url: config.api_endpoint.clone(),
5461
store: TokenStore::new()?,
5562
})
5663
}
@@ -87,9 +94,11 @@ impl ApiKeyManager {
8794
expires_in_days,
8895
};
8996

97+
let url = format!("{}/api/v1/account/api-keys", self.base_url);
9098
let response = self
9199
.client
92-
.post_with_auth("/api/v1/account/api-keys", access_token)
100+
.post(&url)
101+
.header("Authorization", format!("Bearer {}", access_token))
93102
.json(&request)
94103
.send()
95104
.await?
@@ -157,9 +166,11 @@ impl ApiKeyManager {
157166
last_4: String,
158167
) -> Result<()> {
159168
// Test the API key by making a request
169+
let url = format!("{}/api/v1/auth/profile", self.base_url);
160170
let response = self
161171
.client
162-
.get_with_api_key("/api/v1/auth/profile", &api_key)
172+
.get(&url)
173+
.header("X-API-Key", &api_key)
163174
.send()
164175
.await?;
165176

@@ -197,9 +208,11 @@ impl ApiKeyManager {
197208
}
198209

199210
pub async fn list_keys(&self, access_token: &str) -> Result<()> {
211+
let url = format!("{}/api/v1/account/api-keys", self.base_url);
200212
let keys = self
201213
.client
202-
.get_with_auth("/api/v1/account/api-keys", access_token)
214+
.get(&url)
215+
.header("Authorization", format!("Bearer {}", access_token))
203216
.send()
204217
.await?
205218
.error_for_status()?
@@ -255,11 +268,10 @@ impl ApiKeyManager {
255268
return Ok(());
256269
}
257270

271+
let url = format!("{}/api/v1/account/api-keys/{}", self.base_url, key_id);
258272
self.client
259-
.delete_with_auth(
260-
&format!("/api/v1/account/api-keys/{}", key_id),
261-
access_token,
262-
)
273+
.delete(&url)
274+
.header("Authorization", format!("Bearer {}", access_token))
263275
.send()
264276
.await?
265277
.error_for_status()?;

src/auth/device_flow.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use std::time;
66
use tokio::time::sleep;
77

88
use super::token_store::{AuthType, StoredCredentials, TokenStore};
9-
use crate::api::ApiClient;
9+
use crate::config::Config;
10+
use reqwest::Client;
1011

1112
#[derive(Debug, Serialize)]
1213
pub struct DeviceFlowRequest {
@@ -46,14 +47,20 @@ pub struct DeviceTokenError {
4647
}
4748

4849
pub struct DeviceAuth {
49-
client: ApiClient,
50+
client: Client,
51+
base_url: String,
5052
store: TokenStore,
5153
}
5254

5355
impl DeviceAuth {
54-
pub fn new(api_endpoint: String) -> Result<Self> {
56+
pub fn new(config: Config) -> Result<Self> {
57+
let client = Client::builder()
58+
.timeout(time::Duration::from_secs(30))
59+
.build()?;
60+
5561
Ok(Self {
56-
client: ApiClient::new(api_endpoint),
62+
client,
63+
base_url: config.api_endpoint.clone(),
5764
store: TokenStore::new()?,
5865
})
5966
}
@@ -117,9 +124,10 @@ impl DeviceAuth {
117124
scope: "full_access".to_string(),
118125
};
119126

127+
let url = format!("{}/api/v1/auth/device/code", self.base_url);
120128
let response = self
121129
.client
122-
.post("/api/v1/auth/device/code")
130+
.post(&url)
123131
.json(&request)
124132
.send()
125133
.await?
@@ -146,9 +154,10 @@ impl DeviceAuth {
146154
client_id: "kanuni-cli".to_string(),
147155
};
148156

157+
let url = format!("{}/api/v1/auth/device/token", self.base_url);
149158
let response = self
150159
.client
151-
.post("/api/v1/auth/device/token")
160+
.post(&url)
152161
.json(&request)
153162
.send()
154163
.await?;

src/auth/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl AuthManager {
4444
/// Authenticate using device flow (OAuth)
4545
pub async fn login_device_flow(&self) -> Result<()> {
4646
let config = self.config.read().await;
47-
let device_auth = DeviceAuth::new(config.api_endpoint.clone())?;
47+
let device_auth = DeviceAuth::new(config.clone())?;
4848
device_auth.authenticate().await?;
4949

5050
// Reload credentials
@@ -72,11 +72,11 @@ impl AuthManager {
7272
};
7373

7474
let config = self.config.read().await;
75-
let manager = ApiKeyManager::new(config.api_endpoint.clone())?;
75+
let manager = ApiKeyManager::new(config.clone())?;
7676

7777
manager
7878
.authenticate_with_key(
79-
api_key,
79+
api_key.clone(),
8080
"CLI Key".to_string(),
8181
prefix.to_string(),
8282
last_4.to_string(),
@@ -93,7 +93,7 @@ impl AuthManager {
9393
pub async fn create_api_key(&self) -> Result<()> {
9494
let access_token = self.get_access_token().await?;
9595
let config = self.config.read().await;
96-
let manager = ApiKeyManager::new(config.api_endpoint.clone())?;
96+
let manager = ApiKeyManager::new(config.clone())?;
9797
manager.create_key(&access_token).await?;
9898

9999
// Reload credentials if user chose to use the new key
@@ -106,7 +106,7 @@ impl AuthManager {
106106
pub async fn list_api_keys(&self) -> Result<()> {
107107
let access_token = self.get_access_token().await?;
108108
let config = self.config.read().await;
109-
let manager = ApiKeyManager::new(config.api_endpoint.clone())?;
109+
let manager = ApiKeyManager::new(config.clone())?;
110110
manager.list_keys(&access_token).await
111111
}
112112

@@ -146,13 +146,14 @@ impl AuthManager {
146146
return Ok(access_token.clone());
147147
}
148148

149-
// Need to refresh the token
149+
// Need to refresh the token - clone what we need before dropping guard
150+
let refresh_token_clone = refresh_token.clone();
150151
drop(credentials_guard); // Release read lock
151152

152153
let response = self
153154
.client
154155
.refresh_token(RefreshRequest {
155-
refresh_token: refresh_token.clone(),
156+
refresh_token: refresh_token_clone.clone(),
156157
})
157158
.await
158159
.context("Failed to refresh token. Please login again.")?;
@@ -162,7 +163,7 @@ impl AuthManager {
162163
// Update stored tokens
163164
self.store.update_oauth_tokens(
164165
response.access_token.clone(),
165-
refresh_token.clone(), // Keep same refresh token
166+
refresh_token_clone, // Use cloned refresh token
166167
new_expires_at,
167168
)?;
168169

0 commit comments

Comments
 (0)