Skip to content

Commit 9edfdd4

Browse files
committed
Add example env file, add raw Forge endpoints
1 parent 602b7a6 commit 9edfdd4

File tree

5 files changed

+284
-87
lines changed

5 files changed

+284
-87
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
target/
22
Cargo.lock
33

4+
.env
5+
46
# downloaded metadata during development
57
meta

mcmeta/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MCMETA__BIND_ADDRESS=127.0.0.1:9988
2+
MCMETA__STORAGE_FORMAT__TYPE=json
3+
MCMETA__STORAGE_FORMAT__META_DIRECTORY=../meta

mcmeta/src/app_config.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use serde::Deserialize;
33
use crate::MetaMCError;
44

55
#[derive(Deserialize, Debug)]
6-
#[serde(rename_all = "snake_case", untagged)]
6+
#[serde(rename_all = "snake_case", tag = "type")]
77
pub enum StorageFormat {
88
Json { meta_directory: String },
99
Database,
@@ -18,10 +18,10 @@ pub struct ServerConfig {
1818
impl ServerConfig {
1919
pub fn from_config() -> Result<Self, MetaMCError> {
2020
let config = config::Config::builder()
21-
.add_source(config::Environment::with_prefix("MCMETA"))
22-
.set_default("bind_address", "127.0.0.1:8080")?
23-
.set_default("storage_format", "json")?
24-
.set_default("meta_directory", "meta")?
21+
.add_source(config::Environment::with_prefix("mcmeta").separator("__"))
22+
// .set_default("bind_address", "127.0.0.1:8080")?
23+
// .set_default("storage_format.type", "json")?
24+
// .set_default("storage_format.meta_directory", "meta")?
2525
.build()?;
2626

2727
config.try_deserialize::<'_, Self>().map_err(Into::into)

mcmeta/src/main.rs

Lines changed: 194 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ use axum::{
88
Extension, Router,
99
};
1010
use custom_error::custom_error;
11-
use libmcmeta::models::mojang::MojangVersionManifest;
12-
use serde::Deserialize;
11+
use libmcmeta::models::{
12+
forge::{
13+
ForgeInstallerManifestVersion, ForgeMavenMetadata, ForgeMavenPromotions, ForgeVersion,
14+
ForgeVersionMeta,
15+
},
16+
mojang::{MinecraftVersion, MojangVersionManifest},
17+
};
18+
use serde::{Deserialize, Serialize};
1319
use serde_json::json;
14-
use tracing::info;
20+
use tracing::{debug, info};
1521

1622
mod app_config;
1723
mod download;
24+
mod storage;
1825

1926
custom_error! {pub MetaMCError
2027
MojangMetadata { source: download::mojang::MojangMetadataError } = "Error while downloading Mojang metadata: {source}",
@@ -25,80 +32,10 @@ custom_error! {pub MetaMCError
2532
Json { source: serde_json::Error } = "Error while serializing or deserializing JSON: {source}",
2633
}
2734

28-
impl StorageFormat {
29-
pub async fn initialize_metadata(&self) -> Result<(), MetaMCError> {
30-
match self {
31-
app_config::StorageFormat::Json { meta_directory } => {
32-
let metadata_dir = std::path::Path::new(meta_directory);
33-
if !metadata_dir.exists() {
34-
info!(
35-
"Metadata directory at {} does not exist, creating it",
36-
meta_directory
37-
);
38-
std::fs::create_dir_all(metadata_dir)?;
39-
}
40-
41-
self.initialize_mojang_metadata().await?;
42-
}
43-
app_config::StorageFormat::Database => todo!(),
44-
}
45-
46-
Ok(())
47-
}
48-
49-
pub async fn initialize_mojang_metadata(&self) -> Result<(), MetaMCError> {
50-
match self {
51-
StorageFormat::Json { meta_directory } => {
52-
info!("Checking for Mojang metadata");
53-
let metadata_dir = std::path::Path::new(meta_directory);
54-
let mojang_meta_dir = metadata_dir.join("mojang");
55-
56-
if !mojang_meta_dir.exists() {
57-
info!(
58-
"Mojang metadata directory at {} does not exist, creating it",
59-
mojang_meta_dir.display()
60-
);
61-
std::fs::create_dir_all(&mojang_meta_dir)?;
62-
}
63-
64-
let local_manifest = mojang_meta_dir.join("version_manifest_v2.json");
65-
if !local_manifest.exists() {
66-
info!("Mojang metadata does not exist, downloading it");
67-
let manifest = download::mojang::load_manifest().await?;
68-
let manifest_json = serde_json::to_string_pretty(&manifest)?;
69-
std::fs::write(&local_manifest, manifest_json)?;
70-
}
71-
let manifest = serde_json::from_str::<MojangVersionManifest>(
72-
&std::fs::read_to_string(&local_manifest)?,
73-
)?;
74-
let versions_dir = mojang_meta_dir.join("versions");
75-
if !versions_dir.exists() {
76-
info!(
77-
"Mojang versions directory at {} does not exist, creating it",
78-
versions_dir.display()
79-
);
80-
std::fs::create_dir_all(&versions_dir)?;
81-
}
82-
for version in &manifest.versions {
83-
let version_file = versions_dir.join(format!("{}.json", &version.id));
84-
if !version_file.exists() {
85-
info!(
86-
"Mojang metadata for version {} does not exist, downloading it",
87-
&version.id
88-
);
89-
let version_manifest =
90-
download::mojang::load_version_manifest(&version.url).await?;
91-
let version_manifest_json =
92-
serde_json::to_string_pretty(&version_manifest)?;
93-
std::fs::write(&version_file, version_manifest_json)?;
94-
}
95-
}
96-
}
97-
StorageFormat::Database => todo!(),
98-
}
99-
100-
Ok(())
101-
}
35+
#[derive(Serialize, Debug, Clone)]
36+
pub struct APIResponse<T> {
37+
pub data: Option<T>,
38+
pub error: Option<String>,
10239
}
10340

10441
async fn raw_mojang_manifest(config: Extension<Arc<ServerConfig>>) -> impl IntoResponse {
@@ -112,7 +49,10 @@ async fn raw_mojang_manifest(config: Extension<Arc<ServerConfig>>) -> impl IntoR
11249
)
11350
.unwrap();
11451

115-
axum::Json(manifest)
52+
axum::Json(APIResponse {
53+
data: Some(manifest),
54+
error: None,
55+
})
11656
}
11757
app_config::StorageFormat::Database => todo!(),
11858
}
@@ -131,15 +71,178 @@ async fn raw_mojang_version(
13171
if !version_file.exists() {
13272
return (
13373
axum::http::StatusCode::NOT_FOUND,
134-
axum::Json(json!("Version not found")),
74+
axum::Json(APIResponse {
75+
data: None,
76+
error: Some(format!("Version {} does not exist", version)),
77+
}),
13578
);
13679
}
137-
let manifest = serde_json::from_str::<serde_json::Value>(
80+
let manifest = serde_json::from_str::<MinecraftVersion>(
13881
&std::fs::read_to_string(&version_file).unwrap(),
13982
)
14083
.unwrap();
14184

142-
(axum::http::StatusCode::OK, axum::Json(manifest))
85+
(
86+
axum::http::StatusCode::OK,
87+
axum::Json(APIResponse {
88+
data: Some(manifest),
89+
error: None,
90+
}),
91+
)
92+
}
93+
StorageFormat::Database => todo!(),
94+
}
95+
}
96+
97+
async fn raw_forge_maven_meta(config: Extension<Arc<ServerConfig>>) -> impl IntoResponse {
98+
match &config.storage_format {
99+
StorageFormat::Json { meta_directory } => {
100+
let metadata_dir = std::path::Path::new(meta_directory);
101+
let forge_meta_dir = metadata_dir.join("forge");
102+
let maven_meta_file = forge_meta_dir.join("maven-metadata.json");
103+
let manifest = serde_json::from_str::<ForgeMavenMetadata>(
104+
&std::fs::read_to_string(&maven_meta_file).unwrap(),
105+
)
106+
.unwrap();
107+
108+
(
109+
axum::http::StatusCode::OK,
110+
axum::Json(APIResponse {
111+
data: Some(manifest),
112+
error: None,
113+
}),
114+
)
115+
}
116+
StorageFormat::Database => todo!(),
117+
}
118+
}
119+
120+
async fn raw_forge_promotions(config: Extension<Arc<ServerConfig>>) -> impl IntoResponse {
121+
match &config.storage_format {
122+
StorageFormat::Json { meta_directory } => {
123+
let metadata_dir = std::path::Path::new(meta_directory);
124+
let forge_meta_dir = metadata_dir.join("forge");
125+
let promotions_file = forge_meta_dir.join("promotions_slim.json");
126+
let manifest = serde_json::from_str::<ForgeMavenPromotions>(
127+
&std::fs::read_to_string(&promotions_file).unwrap(),
128+
)
129+
.unwrap();
130+
131+
(
132+
axum::http::StatusCode::OK,
133+
axum::Json(APIResponse {
134+
data: Some(manifest),
135+
error: None,
136+
}),
137+
)
138+
}
139+
StorageFormat::Database => todo!(),
140+
}
141+
}
142+
143+
async fn raw_forge_version(
144+
config: Extension<Arc<ServerConfig>>,
145+
Path(version): Path<String>,
146+
) -> impl IntoResponse {
147+
match &config.storage_format {
148+
StorageFormat::Json { meta_directory } => {
149+
let metadata_dir = std::path::Path::new(meta_directory);
150+
let forge_meta_dir = metadata_dir.join("forge");
151+
let versions_dir = forge_meta_dir.join("version_manifests");
152+
let version_file = versions_dir.join(format!("{}.json", version));
153+
if !version_file.exists() {
154+
return (
155+
axum::http::StatusCode::NOT_FOUND,
156+
axum::Json(APIResponse {
157+
data: None,
158+
error: Some(format!("Version {} does not exist", version)),
159+
}),
160+
);
161+
}
162+
let manifest = serde_json::from_str::<ForgeVersion>(
163+
&std::fs::read_to_string(&version_file).unwrap(),
164+
)
165+
.unwrap();
166+
167+
(
168+
axum::http::StatusCode::OK,
169+
axum::Json(APIResponse {
170+
data: Some(manifest),
171+
error: None,
172+
}),
173+
)
174+
}
175+
StorageFormat::Database => todo!(),
176+
}
177+
}
178+
179+
async fn raw_forge_version_meta(
180+
config: Extension<Arc<ServerConfig>>,
181+
Path(version): Path<String>,
182+
) -> impl IntoResponse {
183+
match &config.storage_format {
184+
StorageFormat::Json { meta_directory } => {
185+
let metadata_dir = std::path::Path::new(meta_directory);
186+
let forge_meta_dir = metadata_dir.join("forge");
187+
let versions_dir = forge_meta_dir.join("files_manifests");
188+
let version_file = versions_dir.join(format!("{}.json", version));
189+
if !version_file.exists() {
190+
return (
191+
axum::http::StatusCode::NOT_FOUND,
192+
axum::Json(APIResponse {
193+
data: None,
194+
error: Some(format!("Version {} does not exist", version)),
195+
}),
196+
);
197+
}
198+
let manifest = serde_json::from_str::<ForgeVersionMeta>(
199+
&std::fs::read_to_string(&version_file).unwrap(),
200+
)
201+
.unwrap();
202+
203+
(
204+
axum::http::StatusCode::OK,
205+
axum::Json(APIResponse {
206+
data: Some(manifest),
207+
error: None,
208+
}),
209+
)
210+
}
211+
StorageFormat::Database => todo!(),
212+
}
213+
}
214+
215+
async fn raw_forge_version_installer(
216+
config: Extension<Arc<ServerConfig>>,
217+
Path(version): Path<String>,
218+
) -> impl IntoResponse {
219+
match &config.storage_format {
220+
StorageFormat::Json { meta_directory } => {
221+
let metadata_dir = std::path::Path::new(meta_directory);
222+
let forge_meta_dir = metadata_dir.join("forge");
223+
let versions_dir = forge_meta_dir.join("installer_manifests");
224+
let version_file = versions_dir.join(format!("{}.json", version));
225+
if !version_file.exists() {
226+
return (
227+
axum::http::StatusCode::NOT_FOUND,
228+
axum::Json(APIResponse {
229+
data: None,
230+
error: Some(format!("Version {} does not exist", version)),
231+
}),
232+
);
233+
}
234+
let manifest = serde_json::from_str::<ForgeInstallerManifestVersion>(
235+
&std::fs::read_to_string(&version_file).unwrap(),
236+
)
237+
.unwrap();
238+
239+
(
240+
axum::http::StatusCode::OK,
241+
axum::Json(APIResponse {
242+
data: Some(manifest),
243+
error: None,
244+
}),
245+
)
143246
}
144247
StorageFormat::Database => todo!(),
145248
}
@@ -149,14 +252,23 @@ async fn raw_mojang_version(
149252
async fn main() -> Result<(), MetaMCError> {
150253
tracing_subscriber::fmt::init();
151254
let config = Arc::new(ServerConfig::from_config()?);
255+
debug!("Config: {:#?}", config);
152256

153257
config.storage_format.initialize_metadata().await?;
154258

155259
let raw_mojang_routes = Router::new()
156260
.route("/", get(raw_mojang_manifest))
157261
.route("/:version", get(raw_mojang_version));
262+
let raw_forge_routes = Router::new()
263+
.route("/", get(raw_forge_maven_meta))
264+
.route("/promotions", get(raw_forge_promotions))
265+
.route("/:version", get(raw_forge_version))
266+
.route("/:version/meta", get(raw_forge_version_meta))
267+
.route("/:version/installer", get(raw_forge_version_installer));
158268

159-
let raw_routes = Router::new().nest("/mojang", raw_mojang_routes);
269+
let raw_routes = Router::new()
270+
.nest("/mojang", raw_mojang_routes)
271+
.nest("/forge", raw_forge_routes);
160272

161273
let http = Router::new()
162274
.nest("/raw", raw_routes)

0 commit comments

Comments
 (0)