fix: use our own result types instead of tenors types
add user auth
This commit is contained in:
@@ -1,12 +1,18 @@
|
|||||||
use std::sync::Arc;
|
use axum::{
|
||||||
|
extract::{Query, State},
|
||||||
use axum::{extract::{Query, State}, routing::get, Json, Router};
|
routing::get,
|
||||||
|
Json, Router,
|
||||||
|
};
|
||||||
|
use revolt_database::User;
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
use revolt_database::User;
|
|
||||||
|
|
||||||
use crate::{AppState, tenor::{Tenor, types}};
|
use crate::{
|
||||||
|
tenor,
|
||||||
|
types,
|
||||||
|
AppState,
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn router() -> Router<AppState> {
|
pub async fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
@@ -43,7 +49,7 @@ async fn root() -> Json<RootResponse> {
|
|||||||
struct SearchQueryParams {
|
struct SearchQueryParams {
|
||||||
pub query: String,
|
pub query: String,
|
||||||
pub locale: String,
|
pub locale: String,
|
||||||
pub position: Option<String>
|
pub position: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -56,11 +62,13 @@ struct SearchQueryParams {
|
|||||||
async fn search(
|
async fn search(
|
||||||
_user: User,
|
_user: User,
|
||||||
Query(params): Query<SearchQueryParams>,
|
Query(params): Query<SearchQueryParams>,
|
||||||
State(tenor): State<Tenor>,
|
State(tenor): State<tenor::Tenor>,
|
||||||
) -> Result<Json<Arc<types::SearchResponse>>> {
|
) -> Result<Json<types::SearchResponse>> {
|
||||||
// Todo
|
// Todo
|
||||||
tenor.search(¶ms.query, ¶ms.locale, params.position.as_deref())
|
tenor
|
||||||
|
.search(¶ms.query, ¶ms.locale, params.position.as_deref())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_error!(InternalError))
|
.map_err(|_| create_error!(InternalError))
|
||||||
|
.map(|results| results.as_ref().clone().into())
|
||||||
.map(Json)
|
.map(Json)
|
||||||
}
|
}
|
||||||
@@ -15,11 +15,12 @@ use crate::tenor::Tenor;
|
|||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod tenor;
|
mod tenor;
|
||||||
|
mod types;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
pub database: Database,
|
pub database: Database,
|
||||||
pub tenor: Tenor
|
pub tenor: Tenor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRef<AppState> for Database {
|
impl FromRef<AppState> for Database {
|
||||||
@@ -45,11 +46,15 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
modifiers(&SecurityAddon),
|
modifiers(&SecurityAddon),
|
||||||
paths(
|
paths(
|
||||||
api::root,
|
api::root,
|
||||||
|
api::search,
|
||||||
),
|
),
|
||||||
components(
|
components(
|
||||||
schemas(
|
schemas(
|
||||||
revolt_result::Error,
|
revolt_result::Error,
|
||||||
revolt_result::ErrorType,
|
revolt_result::ErrorType,
|
||||||
|
types::SearchResponse,
|
||||||
|
types::MediaObject,
|
||||||
|
types::MediaResult,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
@@ -70,8 +75,11 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
|
|
||||||
let config = config().await;
|
let config = config().await;
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
database: DatabaseInfo::Auto.connect().await.expect("Unable to connect to database"),
|
database: DatabaseInfo::Auto
|
||||||
tenor: tenor::Tenor::new(&config.api.security.tenor_key)
|
.connect()
|
||||||
|
.await
|
||||||
|
.expect("Unable to connect to database"),
|
||||||
|
tenor: tenor::Tenor::new(&config.api.security.tenor_key),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configure Axum and router
|
// Configure Axum and router
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
use reqwest::Client;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
use lru_time_cache::LruCache;
|
use lru_time_cache::LruCache;
|
||||||
use urlencoding::encode as url_encode;
|
use reqwest::Client;
|
||||||
use revolt_coalesced::{CoalescionService, CoalescionServiceConfig};
|
use revolt_coalesced::{CoalescionService, CoalescionServiceConfig};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use urlencoding::encode as url_encode;
|
||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ pub struct Tenor {
|
|||||||
pub key: Arc<str>,
|
pub key: Arc<str>,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub coalescion: CoalescionService<String, Result<Arc<types::SearchResponse>, TenorError>>,
|
pub coalescion: CoalescionService<String, Result<Arc<types::SearchResponse>, TenorError>>,
|
||||||
pub cache: Arc<RwLock<LruCache<String, Arc<types::SearchResponse>>>>
|
pub cache: Arc<RwLock<LruCache<String, Arc<types::SearchResponse>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tenor {
|
impl Tenor {
|
||||||
@@ -34,18 +34,23 @@ impl Tenor {
|
|||||||
coalescion: CoalescionService::from_config(CoalescionServiceConfig {
|
coalescion: CoalescionService::from_config(CoalescionServiceConfig {
|
||||||
max_concurrent: Some(100),
|
max_concurrent: Some(100),
|
||||||
queue_requests: true,
|
queue_requests: true,
|
||||||
max_queue: None
|
max_queue: None,
|
||||||
}),
|
}),
|
||||||
cache: Arc::new(RwLock::new(cache)),
|
cache: Arc::new(RwLock::new(cache)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search(&self, query: &str, locale: &str, position: Option<&str>) -> Result<Arc<types::SearchResponse>, TenorError> {
|
pub async fn search(
|
||||||
|
&self,
|
||||||
|
query: &str,
|
||||||
|
locale: &str,
|
||||||
|
position: Option<&str>,
|
||||||
|
) -> Result<Arc<types::SearchResponse>, TenorError> {
|
||||||
let unique_key = format!("{query}:{locale}:{position:?}");
|
let unique_key = format!("{query}:{locale}:{position:?}");
|
||||||
|
|
||||||
if self.cache.read().await.contains_key(&unique_key) {
|
if self.cache.read().await.contains_key(&unique_key) {
|
||||||
if let Some(response) = self.cache.write().await.get(&unique_key) {
|
if let Some(response) = self.cache.write().await.get(&unique_key) {
|
||||||
return Ok(response.clone())
|
return Ok(response.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +59,7 @@ impl Tenor {
|
|||||||
|
|
||||||
async move {
|
async move {
|
||||||
let mut url = format!(
|
let mut url = format!(
|
||||||
"{TENOR_API_BASE_URL}/search?key={}&q={}&client_key=Gifbox&locale={}&contentfilter=medium&limit=1",
|
"{TENOR_API_BASE_URL}/search?key={}&q={}&client_key=Gifbox&media_filter=webm,tinywebm&locale={}&contentfilter=medium&limit=1",
|
||||||
&self.key,
|
&self.key,
|
||||||
url_encode(query),
|
url_encode(query),
|
||||||
url_encode(locale),
|
url_encode(locale),
|
||||||
@@ -86,44 +91,3 @@ impl Tenor {
|
|||||||
(*res).clone()
|
(*res).clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use revolt_config::config;
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
|
||||||
async fn test() {
|
|
||||||
let config = config().await;
|
|
||||||
|
|
||||||
let tenor = Tenor::new(&config.api.security.tenor_key);
|
|
||||||
|
|
||||||
let results =tenor.search("amog", "en_US", None).await.unwrap();
|
|
||||||
|
|
||||||
let result = &results.results[0];
|
|
||||||
|
|
||||||
println!("{:?}", result.media_formats.iter().next().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
|
||||||
async fn test_2() {
|
|
||||||
let config = config().await;
|
|
||||||
let tenor = Tenor::new(&config.api.security.tenor_key);
|
|
||||||
|
|
||||||
let mut tasks = Vec::new();
|
|
||||||
|
|
||||||
for i in 1..1001 {
|
|
||||||
let tenor = tenor.clone();
|
|
||||||
println!("creating search task {i}");
|
|
||||||
|
|
||||||
tasks.push((i, tokio::spawn(async move {
|
|
||||||
tenor.search(&format!("amog-{i}"), "en_US", None).await
|
|
||||||
})));
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, task) in tasks {
|
|
||||||
task.await.unwrap().unwrap();
|
|
||||||
println!("Got result for {i}");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct SearchResponse {
|
pub struct SearchResponse {
|
||||||
pub results: Vec<TenorResult>,
|
pub results: Vec<MediaResult>,
|
||||||
pub next: String
|
pub next: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
@@ -14,11 +13,11 @@ pub struct MediaObject {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
pub dims: Vec<u64>,
|
pub dims: Vec<u64>,
|
||||||
pub duration: f64,
|
pub duration: f64,
|
||||||
pub size: f64
|
pub size: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct TenorResult {
|
pub struct MediaResult {
|
||||||
pub created: f64,
|
pub created: f64,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub hasaudio: bool,
|
pub hasaudio: bool,
|
||||||
@@ -32,5 +31,5 @@ pub struct TenorResult {
|
|||||||
pub hascaption: bool,
|
pub hascaption: bool,
|
||||||
pub flags: Vec<String>,
|
pub flags: Vec<String>,
|
||||||
pub bg_color: Option<String>,
|
pub bg_color: Option<String>,
|
||||||
pub url: String
|
pub url: String,
|
||||||
}
|
}
|
||||||
69
crates/services/gifbox/src/types.rs
Normal file
69
crates/services/gifbox/src/types.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::tenor::types;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Clone, Debug, PartialEq)]
|
||||||
|
/// Response containing the current results and the id of the next result for pagination.
|
||||||
|
pub struct SearchResponse {
|
||||||
|
/// Current gif results.
|
||||||
|
pub results: Vec<MediaResult>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Id of the next result.
|
||||||
|
pub next: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Clone, Debug, PartialEq)]
|
||||||
|
/// Indivual gif result.
|
||||||
|
pub struct MediaResult {
|
||||||
|
/// Unique Tenor id.
|
||||||
|
pub id: String,
|
||||||
|
/// Mapping of each file format and url of the file.
|
||||||
|
pub media_formats: HashMap<String, MediaObject>,
|
||||||
|
/// Public Tenor web url for the gif.
|
||||||
|
pub url: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ToSchema, Clone, Debug, PartialEq)]
|
||||||
|
/// Represents the gif in a certain file format.
|
||||||
|
pub struct MediaObject {
|
||||||
|
/// File url of the gif in a certain format.
|
||||||
|
pub url: String,
|
||||||
|
/// Width and height of the file in px.
|
||||||
|
pub dimensions: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl From<types::SearchResponse> for SearchResponse {
|
||||||
|
fn from(value: types::SearchResponse) -> Self {
|
||||||
|
Self {
|
||||||
|
results: value.results.into_iter().map(|result| result.into()).collect(),
|
||||||
|
next: if value.next.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(value.next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<types::MediaResult> for MediaResult {
|
||||||
|
fn from(value: types::MediaResult) -> Self {
|
||||||
|
Self {
|
||||||
|
id: value.id,
|
||||||
|
media_formats: value.media_formats.into_iter().map(|(k, v)| (k, v.into())).collect(),
|
||||||
|
url: value.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<types::MediaObject> for MediaObject {
|
||||||
|
fn from(value: types::MediaObject) -> Self {
|
||||||
|
Self {
|
||||||
|
url: value.url,
|
||||||
|
dimensions: value.dims
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user