Files
gitks/service/internal_auth.rs
T
zhenyi 420dedbc1e feat(service): expand service layer with new domain operations
- Add IM service modules: audit, channel roles, custom emojis, forum
  tags, integrations, invitations, repo links, slash commands, stages,
  voice, webhooks
- Add PR service modules: review requests, templates
- Add repo service modules: contributors, release assets, git extras
  (archive, branch rename, commit extras, diff/merge, tag, tree)
- Add user service: social (follow/block)
- Add internal auth service
- Update existing service modules with expanded functionality
- Remove deleted IM modules: articles, delivery trace, drafts,
  follows, messages, polls, presence, reactions, threads
2026-06-10 18:49:32 +08:00

98 lines
2.6 KiB
Rust

use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::cache::redis::AppRedis;
use crate::error::{AppError, AppResult};
const API_KEY_PREFIX: &str = "internal:auth:";
const DEFAULT_TTL_SECS: u64 = 86400 * 30;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceIdentity {
pub service_name: String,
pub service_id: String,
pub scopes: Vec<String>,
pub issued_at: i64,
pub expires_at: i64,
}
#[derive(Clone)]
pub struct InternalAuthService {
redis: AppRedis,
}
impl InternalAuthService {
pub fn new(redis: AppRedis) -> Self {
Self { redis }
}
pub async fn issue_api_key(
&self,
service_name: &str,
scopes: Vec<String>,
ttl_secs: Option<u64>,
) -> AppResult<(String, ServiceIdentity)> {
let ttl = ttl_secs.unwrap_or(DEFAULT_TTL_SECS);
let now = chrono::Utc::now().timestamp();
let expires_at = now + ttl as i64;
let identity = ServiceIdentity {
service_name: service_name.to_string(),
service_id: Uuid::now_v7().to_string(),
scopes,
issued_at: now,
expires_at,
};
let api_key = format!("im_{}", Uuid::now_v7());
let key = format!("{API_KEY_PREFIX}{api_key}");
let json = serde_json::to_string(&identity)?;
let mut conn = self.redis.get_connection();
redis::Cmd::new()
.arg("SETEX")
.arg(&key)
.arg(ttl)
.arg(&json)
.query_async::<()>(&mut conn)
.await
.map_err(AppError::Redis)?;
Ok((api_key, identity))
}
pub async fn verify_api_key(&self, api_key: &str) -> AppResult<Option<ServiceIdentity>> {
let key = format!("{API_KEY_PREFIX}{api_key}");
let mut conn = self.redis.get_connection();
let json: Option<String> = redis::Cmd::new()
.arg("GET")
.arg(&key)
.query_async(&mut conn)
.await
.map_err(AppError::Redis)?;
match json {
Some(j) => {
let identity: ServiceIdentity = serde_json::from_str(&j)?;
Ok(Some(identity))
}
None => Ok(None),
}
}
pub async fn revoke_api_key(&self, api_key: &str) -> AppResult<()> {
let key = format!("{API_KEY_PREFIX}{api_key}");
let mut conn = self.redis.get_connection();
redis::Cmd::new()
.arg("DEL")
.arg(&key)
.query_async::<()>(&mut conn)
.await
.map_err(AppError::Redis)?;
Ok(())
}
}