Auth rpc
Search Bar Search requests
This commit is contained in:
@@ -2,7 +2,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tonic_build::configure()
|
||||
.build_server(false)
|
||||
.build_client(true)
|
||||
.compile_protos(&["..\\AobaServer\\Proto\\Aoba.proto"], &["..\\AobaServer\\Proto\\"])?;
|
||||
.compile_protos(
|
||||
&["..\\AobaServer\\Proto\\Aoba.proto", "..\\AobaServer\\Proto\\Auth.proto"],
|
||||
&["..\\AobaServer\\Proto\\"],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ use dioxus::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
pub struct ButtonProps {
|
||||
variant: Option<ButtonVariant>,
|
||||
text: String,
|
||||
pub variant: Option<ButtonVariant>,
|
||||
pub text: String,
|
||||
pub onclick: Option<EventHandler<Event<MouseData>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
@@ -16,6 +17,13 @@ pub enum ButtonVariant {
|
||||
#[component]
|
||||
pub fn Button(props: ButtonProps) -> Element {
|
||||
rsx! {
|
||||
button { "{props.text}" }
|
||||
button {
|
||||
onclick: move |event| {
|
||||
if let Some(h) = props.onclick {
|
||||
h.call(event);
|
||||
}
|
||||
},
|
||||
"{props.text}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ pub struct InputProps {
|
||||
pub label: Option<String>,
|
||||
pub placeholder: Option<String>,
|
||||
pub name: String,
|
||||
pub oninput: Option<EventHandler<FormEvent>>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use tonic::{metadata::MetadataValue, IntoRequest, Request};
|
||||
use tonic::{IntoRequest, Request};
|
||||
|
||||
use crate::rpc::{
|
||||
aoba::{MediaModel, PageFilter},
|
||||
get_rpc_client,
|
||||
use crate::{
|
||||
components::MediaItem,
|
||||
rpc::{aoba::PageFilter, get_rpc_client},
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
pub struct MediaGridProps {
|
||||
pub query: Option<String>,
|
||||
#[props(default = Some(1))]
|
||||
pub page: Option<i32>,
|
||||
#[props(default = Some(100))]
|
||||
pub page_size: Option<i32>,
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ impl Into<PageFilter> for MediaGridProps {
|
||||
|
||||
#[component]
|
||||
pub fn MediaGrid(props: MediaGridProps) -> Element {
|
||||
let media_result = use_resource(|| async move {
|
||||
let media_result = use_resource(use_reactive!(|(props,)| async move {
|
||||
let mut client = get_rpc_client();
|
||||
let mut req = Request::new(PageFilter::default());
|
||||
let mut req = Request::new(props.into());
|
||||
req.metadata_mut()
|
||||
.insert("authorization", "Bearer <toto: get token>".parse().unwrap());
|
||||
let result = client.list_media(req).await;
|
||||
return result.expect("Failed to load media").into_inner();
|
||||
});
|
||||
}));
|
||||
|
||||
match &*media_result.read_unchecked() {
|
||||
Some(result) => rsx! {
|
||||
@@ -49,53 +49,16 @@ pub fn MediaGrid(props: MediaGridProps) -> Element {
|
||||
class: "mediaGrid",
|
||||
{result.items.iter().map(|itm| rsx!{
|
||||
MediaItem { item: itm.clone() }
|
||||
})}
|
||||
})},
|
||||
}
|
||||
},
|
||||
None => rsx!(),
|
||||
}
|
||||
// let items = media_result..unwrap().items;
|
||||
// rsx! {
|
||||
// div{
|
||||
// class: "mediaGrid",
|
||||
// {items.iter().map(|itm| rsx!{
|
||||
// MediaItem { item: itm.clone() }
|
||||
// })}
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
pub struct MediaItemProps {
|
||||
pub item: MediaModel,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn MediaItem(props: MediaItemProps) -> Element {
|
||||
let filename = props.item.file_name;
|
||||
let id = props.item.id.unwrap().value;
|
||||
let mtype = props.item.media_type.to_string();
|
||||
// let url = "https://aoba.app/i/{}";
|
||||
rsx! {
|
||||
None => rsx! {
|
||||
div{
|
||||
class: "mediaItem",
|
||||
img{ src: "https://aoba.app/i/{id}" }
|
||||
class: "mediaGrid",
|
||||
div {
|
||||
class: "info",
|
||||
span{
|
||||
class: "name",
|
||||
"{filename}"
|
||||
},
|
||||
div{
|
||||
class: "details",
|
||||
span{
|
||||
"{mtype}"
|
||||
},
|
||||
span{
|
||||
"{props.item.view_count}"
|
||||
"No results could be loaded"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
AobaClient/src/components/media_item.rs
Normal file
43
AobaClient/src/components/media_item.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::rpc::aoba::MediaModel;
|
||||
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
pub struct MediaItemProps {
|
||||
pub item: MediaModel,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn MediaItem(props: MediaItemProps) -> Element {
|
||||
let mtype = props.item.media_type().as_str_name();
|
||||
let filename = props.item.file_name;
|
||||
let id = props.item.media_id.unwrap().value;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let src = format!("http://localhost:5164/m/{id}");
|
||||
#[cfg(not(debug_assertions))]
|
||||
let src = format!("https://aoba.app/m/{id}");
|
||||
// let url = "https://aoba.app/i/{}";
|
||||
rsx! {
|
||||
div{
|
||||
class: "mediaItem",
|
||||
img{ src: src }
|
||||
div {
|
||||
class: "info",
|
||||
span{
|
||||
class: "name",
|
||||
"{filename}"
|
||||
},
|
||||
div{
|
||||
class: "details",
|
||||
span{
|
||||
"{mtype}"
|
||||
},
|
||||
span{
|
||||
"{props.item.view_count}"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
pub mod basic;
|
||||
mod media_grid;
|
||||
mod media_item;
|
||||
mod navbar;
|
||||
mod search;
|
||||
pub use media_grid::*;
|
||||
pub use media_item::*;
|
||||
pub use navbar::*;
|
||||
pub use search::*;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Search() -> Element {
|
||||
pub fn Search(query: Option<String>, oninput: EventHandler<FormEvent>) -> Element {
|
||||
rsx! {
|
||||
div{
|
||||
class: "searchBar",
|
||||
input {
|
||||
type: "search",
|
||||
placeholder: "Search Files"
|
||||
placeholder: "Search Files",
|
||||
value: query.unwrap_or("".into()),
|
||||
oninput: move |event| oninput.call(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,18 @@ use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Home() -> Element {
|
||||
let mut query = use_signal(|| "".to_string());
|
||||
|
||||
rsx! {
|
||||
div { id: "content",
|
||||
Search { },
|
||||
MediaGrid { }
|
||||
div {
|
||||
id: "content",
|
||||
Search {
|
||||
query: query.cloned(),
|
||||
oninput: move |event:FormEvent| {
|
||||
query.set(event.value())
|
||||
}
|
||||
},
|
||||
MediaGrid { query: query.cloned() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
|
||||
<PackageReference Include="MaybeError" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
||||
<PackageReference Include="MongoDB.Analyzer" Version="1.5.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.3.0" />
|
||||
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.0.0" />
|
||||
|
||||
26
AobaCore/AobaIndexCreationService.cs
Normal file
26
AobaCore/AobaIndexCreationService.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using AobaCore.Models;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace AobaCore;
|
||||
|
||||
public class AobaIndexCreationService(IMongoDatabase db): BackgroundService
|
||||
{
|
||||
private readonly IMongoCollection<Media> _media = db.GetCollection<Media>("media");
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
var textKeys = Builders<Media>.IndexKeys
|
||||
.Text(m => m.Filename);
|
||||
|
||||
var textModel = new CreateIndexModel<Media>(textKeys, new CreateIndexOptions
|
||||
{
|
||||
Name = "Text",
|
||||
Background = true
|
||||
});
|
||||
|
||||
await _media.EnsureIndexAsync(textModel);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,20 @@ public static class Extensions
|
||||
services.AddSingleton(dbClient);
|
||||
services.AddSingleton<IMongoDatabase>(db);
|
||||
services.AddSingleton<AobaService>();
|
||||
services.AddHostedService<AobaIndexCreationService>();
|
||||
return services;
|
||||
}
|
||||
|
||||
public static async Task EnsureIndexAsync<T>(this IMongoCollection<T> collection, CreateIndexModel<T> indexModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
await collection.Indexes.CreateOneAsync(indexModel);
|
||||
}
|
||||
catch (MongoCommandException e) when (e.Code == 85 || e.Code == 86) //CodeName "IndexOptionsConflict" or "NameConflict"
|
||||
{
|
||||
await collection.Indexes.DropOneAsync(indexModel.Options.Name);
|
||||
await collection.Indexes.CreateOneAsync(indexModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Proto\Aoba.proto"></Protobuf>
|
||||
<Protobuf Include="Proto\Auth.proto"></Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,16 +16,12 @@ public class MediaController(AobaService aobaService, ILogger<MediaController> l
|
||||
[ResponseCache(Duration = int.MaxValue)]
|
||||
public async Task<IActionResult> MediaAsync(ObjectId id, [FromServices] MongoClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
using var session = await client.StartSessionAsync(cancellationToken: cancellationToken);
|
||||
session.StartTransaction();
|
||||
var file = await aobaService.GetFileStreamAsync(id, cancellationToken: cancellationToken);
|
||||
if (file.HasError)
|
||||
{
|
||||
await session.AbortTransactionAsync(cancellationToken: cancellationToken);
|
||||
logger.LogError(file.Error.Exception, "Failed to load media stream");
|
||||
return NotFound();
|
||||
}
|
||||
await session.CommitTransactionAsync(cancellationToken: cancellationToken);
|
||||
var mime = MimeTypesMap.GetMimeType(file.Value.FileInfo.Filename);
|
||||
_ = aobaService.IncrementFileViewCountAsync(id, cancellationToken);
|
||||
return File(file, mime, true);
|
||||
|
||||
@@ -42,7 +42,16 @@ builder.Services.AddCors(o =>
|
||||
p.AllowAnyOrigin();
|
||||
p.AllowAnyMethod();
|
||||
p.AllowAnyHeader();
|
||||
//p.WithOrigins("http://127.0.0.1:8080", "https://aoba.app");
|
||||
});
|
||||
o.AddPolicy("RPC", p =>
|
||||
{
|
||||
p.AllowAnyMethod();
|
||||
p.AllowAnyHeader();
|
||||
#if DEBUG
|
||||
p.AllowAnyOrigin();
|
||||
#else
|
||||
p.WithOrigins("http://127.0.0.1:8080", "https://aoba.app");
|
||||
#endif
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,7 +116,10 @@ app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
app.MapObserability();
|
||||
app.MapGrpcService<AobaRpcService>()
|
||||
.RequireCors("AllowAll");
|
||||
.RequireCors("RPC");
|
||||
app.MapGrpcService<AobaAuthService>()
|
||||
.AllowAnonymous()
|
||||
.RequireCors("RPC");
|
||||
|
||||
|
||||
|
||||
|
||||
33
AobaServer/Proto/Auth.proto
Normal file
33
AobaServer/Proto/Auth.proto
Normal file
@@ -0,0 +1,33 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option csharp_namespace = "Aoba.RPC.Auth";
|
||||
package aoba.Auth;
|
||||
|
||||
service AuthRpc {
|
||||
rpc Login(Credentials) returns (LoginResponse);
|
||||
rpc LoginPasskey(PassKeyPayload) returns (LoginResponse);
|
||||
}
|
||||
|
||||
message Credentials{
|
||||
string user = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message PassKeyPayload {
|
||||
|
||||
}
|
||||
|
||||
message Jwt{
|
||||
string token = 1;
|
||||
}
|
||||
|
||||
message LoginResponse{
|
||||
oneof result {
|
||||
Jwt jwt = 1;
|
||||
LoginError error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message LoginError{
|
||||
string message = 1;
|
||||
}
|
||||
6
AobaServer/Services/AobaAuthService.cs
Normal file
6
AobaServer/Services/AobaAuthService.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace AobaServer.Services;
|
||||
|
||||
public class AobaAuthService() : Aoba.RPC.Auth.AuthRpc.AuthRpcBase
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace AobaServer.Utils;
|
||||
|
||||
@@ -12,4 +13,6 @@ public static class Extensions
|
||||
return result;
|
||||
return ObjectId.Empty;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user