Implement gRPC on client and server

Pending Testing
This commit is contained in:
2025-05-03 01:09:59 -04:00
parent 84f4dc9b8e
commit 0239186a13
12 changed files with 475 additions and 15 deletions

257
AobaClient/Cargo.lock generated
View File

@@ -26,13 +26,22 @@ dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "aoba-client"
version = "0.1.0"
dependencies = [
"dioxus",
"prost",
"serde",
"serde_repr",
"tonic",
"tonic-build",
]
[[package]]
@@ -206,6 +215,51 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
dependencies = [
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"itoa 1.0.15",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
version = "0.3.74"
@@ -1309,6 +1363,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "endi"
version = "1.1.0"
@@ -1429,6 +1489,12 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.1"
@@ -1954,6 +2020,25 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "h2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap 2.9.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "half"
version = "2.6.0"
@@ -2069,6 +2154,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.6.0"
@@ -2078,9 +2169,11 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa 1.0.15",
"pin-project-lite",
"smallvec",
@@ -2088,6 +2181,19 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-timeout"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]]
name = "hyper-util"
version = "0.1.11"
@@ -2297,6 +2403,15 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
@@ -2580,6 +2695,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.7.4"
@@ -2670,6 +2791,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "multimap"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "ndk"
version = "0.9.0"
@@ -3036,6 +3163,16 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "petgraph"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [
"fixedbitset",
"indexmap 2.9.0",
]
[[package]]
name = "phf"
version = "0.8.0"
@@ -3245,6 +3382,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "prettyplease"
version = "0.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
dependencies = [
"proc-macro2",
"syn 2.0.100",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
@@ -3316,6 +3463,58 @@ dependencies = [
"version_check",
]
[[package]]
name = "prost"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-build"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
dependencies = [
"heck 0.5.0",
"itertools",
"log",
"multimap",
"once_cell",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
"syn 2.0.100",
"tempfile",
]
[[package]]
name = "prost-derive"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "prost-types"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
dependencies = [
"prost",
]
[[package]]
name = "quote"
version = "1.0.40"
@@ -4225,6 +4424,17 @@ dependencies = [
"syn 2.0.100",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.14"
@@ -4283,6 +4493,49 @@ dependencies = [
"winnow",
]
[[package]]
name = "tonic"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85839f0b32fd242bb3209262371d07feda6d780d16ee9d2bc88581b89da1549b"
dependencies = [
"async-trait",
"axum",
"base64",
"bytes",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost",
"socket2",
"tokio",
"tokio-stream",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic-build"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d85f0383fadd15609306383a90e85eaed44169f931a5d2be1b42c76ceff1825e"
dependencies = [
"prettyplease",
"proc-macro2",
"prost-build",
"prost-types",
"quote",
"syn 2.0.100",
]
[[package]]
name = "tower"
version = "0.5.2"
@@ -4291,11 +4544,15 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"indexmap 2.9.0",
"pin-project-lite",
"slab",
"sync_wrapper",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]

View File

@@ -10,6 +10,11 @@ edition = "2021"
dioxus = { version = "0.6.0", features = ["router"] }
serde = "1.0.219"
serde_repr = "0.1.20"
tonic = "*"
prost = "0.13"
[build-dependencies]
tonic-build = "*"
[features]
default = ["web"]

7
AobaClient/build.rs Normal file
View File

@@ -0,0 +1,7 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.build_server(false)
.compile_protos(&["..\\AobaServer\\Proto\\Aoba.proto"], &["..\\AobaServer\\Proto\\"])?;
Ok(())
}

View File

@@ -13,6 +13,7 @@ pub enum ButtonVariant {
Accented,
}
#[component]
pub fn Button(props: ButtonProps) -> Element {
rsx! {
button { "{props.text}" }

View File

@@ -2,6 +2,7 @@ pub mod components;
mod layouts;
pub mod models;
pub mod route;
pub mod rpc;
pub mod views;
use dioxus::prelude::*;

37
AobaClient/src/rpc.rs Normal file
View File

@@ -0,0 +1,37 @@
use std::sync::RwLock;
use aoba::aoba_rpc_client::AobaRpcClient;
use tonic::transport::Channel;
pub mod aoba {
tonic::include_proto!("aoba");
}
static RPC_CLIENT: RpcConnection = RpcConnection {
client: RwLock::new(None),
};
#[derive(Default)]
pub struct RpcConnection {
client: RwLock<Option<AobaRpcClient<Channel>>>,
}
impl RpcConnection {
pub async fn get_client(&self) -> AobaRpcClient<Channel> {
self.ensure_client().await;
return self.client.read().unwrap().clone().unwrap();
}
async fn ensure_client(&self) {
if self.client.read().unwrap().is_none() {
let c = AobaRpcClient::connect("http://localhost:5000")
.await
.expect("Failed to connect RPC");
*self.client.write().unwrap() = Some(c);
}
}
}
pub async fn get_rpc_client() -> AobaRpcClient<Channel> {
return RPC_CLIENT.get_client().await;
}

View File

@@ -18,6 +18,18 @@ public class AobaService(IMongoDatabase db)
return await _media.Find(m => m.Id == id).FirstOrDefaultAsync(cancellationToken);
}
public async Task<PagedResult<Media>> FindMediaAsync(string? query, int page = 1, int pageSize = 50)
{
var filter = string.IsNullOrWhiteSpace(query) ? "{}" : Builders<Media>.Filter.Text(query);
var find = _media.Find(filter);
var total = await find.CountDocumentsAsync();
page -= 1;
var items = await find.Skip(page * pageSize).Limit(pageSize).ToListAsync();
return new PagedResult<Media>(items, page, pageSize, total);
}
public Task AddMediaAsync(Media media, CancellationToken cancellationToken = default)
{
return _media.InsertOneAsync(media, null, cancellationToken);

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AobaCore.Models;
public class PagedResult<T>(List<T> items, int page, int pageSize, long totalItems)
{
public List<T> Items { get; set; } = items;
public int Page { get; set; } = page;
public int PageSize { get; set; } = pageSize;
public long TotalItems { get; set; } = totalItems;
public long TotalPages { get; set; } = totalItems / pageSize;
public string? Query { get; set; }
}

View File

@@ -1,23 +1,53 @@
syntax = "proto3";
option csharp_namespace = "Aoba.RPC";
package aoba;
service AobaRPC {
rpc GetMedia (Id) returns (MediaModel);
service AobaRpc {
rpc GetMedia (Id) returns (MediaResponse);
rpc ListMedia(PageFilter) returns (ListResponse);
}
message PageFilter{
optional int32 page = 1;
optional int32 pageSize = 2;
optional string query = 3;
}
message Id{
string idString = 1;
string value = 1;
}
message MediaResponse {
oneof result {
MediaModel value = 1;
Empty empty = 2;
}
}
message ListResponse{
repeated MediaModel items = 1;
Pagination pagination = 2;
}
message Pagination {
int32 page = 1;
int32 pageSize = 2;
int64 totalPages = 3;
int64 totalItems = 4;
optional string query = 5;
}
message Empty{}
message MediaModel {
int32 version = 1;
Id id = 2;
string mediaId = 3;
string fileName = 4;
MediaType mediaType = 5;
string ext = 6;
int32 viewCount = 7;
Id owner = 8;
Id id = 1;
Id mediaId = 2;
string fileName = 3;
MediaType mediaType = 4;
string ext = 5;
int32 viewCount = 6;
Id owner = 7;
}
enum MediaType{
@@ -27,4 +57,4 @@ enum MediaType{
Text = 3;
Code = 4;
Raw = 5;
}
}

View File

@@ -1,13 +1,25 @@
using AobaCore;
using Aoba.RPC;
using AobaServer.Utils;
using Grpc.Core;
namespace AobaServer.Services;
public class AobaRpcService(AobaService aobaService) : AobaRPC.AobaRPCBase
public class AobaRpcService(AobaService aobaService) : AobaRpc.AobaRpcBase
{
public override Task<MediaModel> GetMedia(Id request, ServerCallContext context)
public override async Task<MediaResponse> GetMedia(Id request, ServerCallContext context)
{
return base.GetMedia(request, context);
var media = await aobaService.GetMediaAsync(request.ToObjectId());
return media.ToResponse();
}
public override async Task<ListResponse> ListMedia(PageFilter request, ServerCallContext context)
{
var result = await aobaService.FindMediaAsync(request.Query, request.HasPage ? request.Page : 1, request.HasPageSize ? request.PageSize : 50);
return result.ToResponse();
}
}

View File

@@ -0,0 +1,15 @@
using MongoDB.Bson;
namespace AobaServer.Utils;
public static class Extensions
{
public static ObjectId ToObjectId(this string? value)
{
if(value == null)
return ObjectId.Empty;
if(ObjectId.TryParse(value, out ObjectId result))
return result;
return ObjectId.Empty;
}
}

View File

@@ -0,0 +1,65 @@
using AobaCore.Models;
using Aoba.RPC;
using MongoDB.Bson;
namespace AobaServer.Utils;
public static class ProtoExtensions
{
public static ListResponse ToResponse(this PagedResult<Media> result)
{
var res = new ListResponse()
{
Pagination = result.ToPagination(),
};
res.Items.AddRange(result.Items.Select(i => i.ToMediaModel()));
return res;
}
public static Pagination ToPagination<T>(this PagedResult<T> result)
{
return new Pagination()
{
Page = result.Page,
PageSize = result.PageSize,
TotalItems = result.TotalItems,
TotalPages = result.TotalPages,
Query = result.Query,
};
}
public static MediaResponse ToResponse(this Media? media)
{
if(media == null)
return new MediaResponse() { Empty = new Empty() };
return new MediaResponse()
{
Value = media.ToMediaModel()
};
}
public static MediaModel ToMediaModel(this Media media)
{
return new MediaModel()
{
Ext = media.Ext,
FileName = media.Filename,
Id = media.Id.ToId(),
MediaId = media.MediaId.ToId(),
MediaType = (Aoba.RPC.MediaType)media.MediaType,
Owner = media.Owner.ToId(),
ViewCount = media.ViewCount,
};
}
public static Id ToId(this ObjectId id)
{
return new Id() { Value = id.ToString() };
}
public static ObjectId ToObjectId(this Id id)
{
return id.Value.ToObjectId();
}
}