diff --git a/server/src/main.rs b/server/src/main.rs index a134e9b..1b2ae8d 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -18,6 +18,46 @@ struct AppState { website_root: PathBuf, } +async fn git_pull(State(state): State) -> impl IntoResponse { + let output = tokio::process::Command::new("git") + .arg("pull") + .current_dir(&state.website_root) + .output() + .await; + + match output { + Ok(o) => { + let stdout = String::from_utf8_lossy(&o.stdout); + let stderr = String::from_utf8_lossy(&o.stderr); + let status = if o.status.success() { "ok" } else { "error" }; + + info!("git pull stdout: {}", stdout.trim()); + if !stderr.is_empty() { + warn!("git pull stderr: {}", stderr.trim()); + } + + ( + if o.status.success() { + StatusCode::OK + } else { + StatusCode::INTERNAL_SERVER_ERROR + }, + format!( + "status: {}\n\nstdout:\n{}\nstderr:\n{}", + status, stdout, stderr + ), + ) + } + Err(e) => { + warn!("failed to spawn git pull: {}", e); + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("failed to run git pull: {}", e), + ) + } + } +} + #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); @@ -31,9 +71,12 @@ async fn main() { .route("/", get(serve_index)) .route("/post/{file}", get(serve_post_page)) .route("/api/backdrops.json", get(serve_backdrops)) - .route("/api/team.json", get(serve_team)) + .route("/api/staff.json", get(serve_staff)) + .route("/api/teams.json", get(serve_teams)) .route("/api/posts.json", get(serve_posts)) .route("/api/post/{file}", get(serve_post_md)) + .route("/api/update", get(git_pull)) + .route("/directs/{name}", get(serve_direct)) .nest_service("/assets", ServeDir::new(website_root.join("assets"))) .nest_service("/posts", ServeDir::new(website_root.join("posts"))) .fallback(get(serve_static)) @@ -90,8 +133,12 @@ async fn serve_backdrops(State(state): State) -> impl IntoResponse { serve_file(state.website_root.join("api/backdrops.json")).await } -async fn serve_team(State(state): State) -> impl IntoResponse { - serve_file(state.website_root.join("api/team.json")).await +async fn serve_staff(State(state): State) -> impl IntoResponse { + serve_file(state.website_root.join("api/staff.json")).await +} + +async fn serve_teams(State(state): State) -> impl IntoResponse { + serve_file(state.website_root.join("api/teams.json")).await } async fn serve_posts(State(state): State) -> impl IntoResponse { @@ -152,3 +199,40 @@ async fn serve_file(path: PathBuf) -> Response { .body(Body::from(content)) .unwrap() } + +use axum::http::HeaderValue; + +async fn serve_direct( + State(state): State, + Path(name): Path, +) -> impl IntoResponse { + if name.contains('/') || name.contains('\\') || name.starts_with('.') { + return StatusCode::BAD_REQUEST.into_response(); + } + + let path = state.website_root.join("directs").join(&name); + + if !path.exists() || !path.is_file() { + return StatusCode::NOT_FOUND.into_response(); + } + + let content = match fs::read_to_string(&path).await { + Ok(c) => c.trim().to_string(), + Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), + }; + + if content.is_empty() { + return StatusCode::NO_CONTENT.into_response(); + } + + if !content.starts_with("http://") && !content.starts_with("https://") { + warn!("invalid redirect target in {:?}: {}", path, content); + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + } + + Response::builder() + .status(StatusCode::FOUND) // 302 redirect + .header(header::LOCATION, HeaderValue::from_str(&content).unwrap()) + .body(Body::empty()) + .unwrap() +} diff --git a/website/api/posts.json b/website/api/posts.json index 4b6a7d9..f26d1e6 100644 --- a/website/api/posts.json +++ b/website/api/posts.json @@ -1,6 +1,15 @@ [ { - "date": "2026-04-21", + "date": "2026-04-22, by AlexEmmet", + + "head_img": "outpost.png", + "file": "Teams.md", + + "title": "Teams", + "description": "Get to know the teams!" + }, + { + "date": "2026-04-21, by AlexEmmet", "head_img": "village_sunset.png", "file": "JoinAnarchyphase.md", @@ -9,7 +18,7 @@ "description": "Join the Anarchyphase community today!" }, { - "date": "2026-04-21", + "date": "2026-04-21, by AlexEmmet", "head_img": "horse_rainbow.png", "file": "Home.md", @@ -18,7 +27,7 @@ "description": "Learn about the /Home command." }, { - "date": "2026-04-21", + "date": "2026-04-21, by AlexEmmet", "head_img": "horse_rainbow.png", "file": "ThisWebsite.md", @@ -27,7 +36,7 @@ "description": "Learn about this website." }, { - "date": "2026-04-21", + "date": "2026-04-21, by AlexEmmet", "head_img": "horse_rainbow.png", "file": "AlexsOpinion.md", diff --git a/website/api/team.json b/website/api/staff.json similarity index 93% rename from website/api/team.json rename to website/api/staff.json index 2496fc6..40d66d2 100644 --- a/website/api/team.json +++ b/website/api/staff.json @@ -9,7 +9,7 @@ "icon": "ninivx.png", "name": "NiniVX", "priority": 1, - "role": "Administrator" + "role": "Community Manager" }, { "icon": "burrata.png", @@ -18,6 +18,7 @@ "role": "Administrator" }, { + "icon": "pitrex.png", "name": "Pitrex", "priority": 2, "role": "Moderator & Developer" diff --git a/website/api/teams.json b/website/api/teams.json new file mode 100644 index 0000000..c3a781c --- /dev/null +++ b/website/api/teams.json @@ -0,0 +1,44 @@ +[ + { + "title": "Anarchyphase Public Infrastructure", + "acronym": "API", + "leader": "NiniVX", + "vips": "mewjo_", + "description": "Building infrastructure across the server.", + "image": "api.png", + "discord": "directs/api" + }, + { + "title": "Commune", + "acronym": "", + "leader": "StormGamer0512", + "vips": "", + "description": "Spreading Communism around the world", + "image": "campsite.png" + }, + { + "title": "Orbit", + "acronym": "", + "leader": "Yippee", + "vips": "Skely, xLeggings, Luxi", + "description": "Me when I Orb it", + "image": "village_sunset.png" + }, + { + "title": "Chunky Town", + "acronym": "CT", + "leader": "MoriceMC", + "vips": "Yeah he's the owner", + "description": "We are number one", + "image": "icon.png", + "discord": "directs/ct" + }, + { + "title": "Ardubia", + "acronym": "", + "leader": "Liwitikiwi", + "vips": "", + "description": "Build borders to keep out foreign aliens", + "image": "outpost.png" + } +] diff --git a/website/app.js b/website/app.js index 6e0f2c3..b62fd8c 100644 --- a/website/app.js +++ b/website/app.js @@ -124,24 +124,24 @@ async function loadBackdrops() { } } -// ─── Load Team ─── -async function loadTeam() { +// ─── Load Staff (in Socials section) ─── +async function loadStaff() { try { - const response = await fetch("/api/team.json"); - const team = await response.json(); - team.sort((a, b) => a.priority - b.priority); + const response = await fetch("/api/staff.json"); + const staff = await response.json(); + staff.sort((a, b) => a.priority - b.priority); - const container = document.getElementById("team-grid"); + const container = document.getElementById("staff-grid"); if (!container) return; - container.innerHTML = team + container.innerHTML = staff .map( (member) => ` -
+
${ member.icon ? `${member.name}` - : '
' + : '
' }
${member.name}
${member.role}
@@ -150,10 +150,70 @@ async function loadTeam() { ) .join(""); } catch (err) { - console.error("Failed to load team:", err); + console.error("Failed to load staff:", err); } } +// ─── Load Teams ─── +async function loadTeams() { + try { + const response = await fetch("/api/teams.json"); + const teams = await response.json(); + const container = document.getElementById("teams-grid"); + if (!container) return; + + container.innerHTML = teams + .map((team) => { + const acronym = team.acronym || ""; + const vips = team.vips || ""; + const hasDiscord = team.discord && team.discord.trim() !== ""; + + return ` +
+
+
+
+
${team.title}
+ ${acronym ? `
${acronym}
` : ""} +
+
+
+
+
+ ${team.leader} +
+ ${ + vips + ? ` +
+ ${vips} +
+ ` + : "" + } +
+

${team.description}

+ ${ + hasDiscord + ? ` + + + + + Discord + + ` + : "" + } +
+
+ `; + }) + .join(""); + } catch (err) { + console.error("Failed to load teams:", err); + } +} // ─── Load Posts ─── async function loadPosts() { try { @@ -242,7 +302,8 @@ function initScrollEffects() { // ─── Initialize ─── document.addEventListener("DOMContentLoaded", () => { loadBackdrops(); - loadTeam(); + loadStaff(); + loadTeams(); loadPosts(); initVideo(); initScrollEffects(); diff --git a/website/assets/api.png b/website/assets/api.png new file mode 100644 index 0000000..2f1bf03 Binary files /dev/null and b/website/assets/api.png differ diff --git a/website/assets/harrpy.png b/website/assets/harrpy.png deleted file mode 100644 index 5ed9a23..0000000 Binary files a/website/assets/harrpy.png and /dev/null differ diff --git a/website/assets/pitrex.png b/website/assets/pitrex.png new file mode 100644 index 0000000..4be7d9e Binary files /dev/null and b/website/assets/pitrex.png differ diff --git a/website/directs/api b/website/directs/api new file mode 100644 index 0000000..1f29ef4 --- /dev/null +++ b/website/directs/api @@ -0,0 +1 @@ +https://discord.gg/3NJ6ZjWTJR diff --git a/website/directs/ct b/website/directs/ct new file mode 100644 index 0000000..70e162d --- /dev/null +++ b/website/directs/ct @@ -0,0 +1 @@ +https://discord.gg/dKuPGE32bT diff --git a/website/index.html b/website/index.html index 3efac2f..bfb2d35 100644 --- a/website/index.html +++ b/website/index.html @@ -95,47 +95,55 @@
+ +
+

Staff

+
+
-

Team

-
+

Teams

+
diff --git a/website/posts/Teams.md b/website/posts/Teams.md new file mode 100644 index 0000000..3037996 --- /dev/null +++ b/website/posts/Teams.md @@ -0,0 +1,44 @@ +# Teams + +Hello Yall, Alex here, + +Today I added a teams section & filled it with the most important teams I found, at the moment all descriptions are jokes but I hope the leaders will contact me for actual details & icons. + +Now all I know about the teams: + +## Anarchyphase Public Infrastructure +I work with API's a lot, though barely in Minecraft, I took part in designing the doors & walls of the new API base currently under construction. + +Their leader is NiniVX, +and i know mewjo_ personally, quite the nice guy to be aroung. +They are Building public infrastructure across the server (who could guess). + + +## Commune +Led by StormGamer0512 + +They are spreading Communism around the world. +Not really it's just the first thing that comes to mind. +I think they fight a lot. + +## Orbit +Led by Yippee, +with Skely, xLeggings, Luxi, + +They are extremley good at PVP. + +## Chunky Town +Led by the owner of Anarchyphase MoriceMC + +they are building a base inside a chunk. + + +## Ardubia +Led byLiwitikiwi + +They are builing a safe region for builders & non pvpers. +I neither know where nor how. + + +With some fulfilling love +Alex diff --git a/website/style.css b/website/style.css index db056ce..ce14c93 100644 --- a/website/style.css +++ b/website/style.css @@ -775,3 +775,218 @@ footer a:hover { ::-webkit-scrollbar-thumb:hover { background: #3a424a; } + +/* ─── Socials Section Layout ─── */ +#socials .content-wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; +} + +/* Social links come first */ +.social-links { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1.5rem; + margin-bottom: 1rem; +} + +/* Staff section below socials */ +.staff-section { + width: 100%; + max-width: 1200px; +} + +/* Staff title */ +.staff-section h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 3rem; + text-shadow: 0 3px 15px rgba(0, 0, 0, 0.9); + color: #fff; +} + +.staff-card { + padding: 1.8rem 1.2rem; +} + +.staff-placeholder { + width: 80px; + height: 80px; + border-radius: 50%; + background: #2a2e33; + margin: 0 auto 1rem; + border: 3px solid #2a2e33; +} + +/* ─── Team Cards ─── */ +#team { + background: var(--bg-solid); +} + +#team h2 { + text-align: center; + font-size: 2.2rem; + margin-bottom: 3rem; + color: #fff; +} + +.teams-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.team-card-large { + position: relative; + border-radius: 16px; + border: 1px solid var(--border-base); + overflow: hidden; + transition: all 0.3s; + min-height: 420px; + display: flex; + flex-direction: column; +} + +.team-card-large:hover { + transform: translateY(-6px); + border-color: var(--border-hover); +} + +/* background image */ +.team-card-image { + position: absolute; + inset: 0; + background-size: cover; + background-position: center; + z-index: 0; +} + +/* Bottom half */ +.team-card-overlay { + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + transparent 0%, + transparent 35%, + rgba(10, 12, 14, 0.85) 65%, + rgba(10, 12, 14, 0.95) 100% + ); + backdrop-filter: blur(0px); + z-index: 1; +} + +/* Header text centered */ +.team-card-header { + position: relative; + z-index: 2; + text-align: center; + padding: 2rem 1.5rem 1rem; + margin-top: auto; +} + +.team-name { + font-size: 1.5rem; + font-weight: bold; + color: #fff; + text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9); +} + +.team-acronym { + font-size: 0.9rem; + color: var(--accent-light); + margin-top: 0.3rem; + letter-spacing: 0.15em; + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); +} + +.team-card-body { + position: relative; + z-index: 2; + padding: 0 1.5rem 1.5rem; + margin-top: auto; +} + +/* Fade divider lines */ +.fade-line { + height: 1px; + background: linear-gradient( + to right, + transparent 0%, + var(--border-hover) 30%, + var(--border-hover) 70%, + transparent 100% + ); + margin: 0.8rem 0; + opacity: 0.5; +} + +.team-meta-row { + display: flex; + justify-content: center; + align-items: center; +} + +.meta-label { + font-size: 1.1rem; + color: #fff; + font-weight: 600; + text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8); +} + +.meta-value.vips { + font-size: 0.9rem; + color: var(--text-muted); + letter-spacing: 0.15em; +} + +.team-description { + color: #bbb; + line-height: 1.6; + font-size: 0.95rem; + text-align: center; + margin: 0.5rem 0 1rem; +} + +/* Discord button centered at bottom */ +.team-discord-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 0.6rem; + width: calc(100% - 1rem); + margin: 0.5rem auto 0; + padding: 0.7rem 1rem; + background: rgba(88, 101, 242, 0.2); + border: 1px solid rgba(88, 101, 242, 0.35); + border-radius: 10px; + color: #aab2ff; + text-decoration: none; + font-size: 0.9rem; + font-weight: 500; + transition: all 0.3s; +} + +.team-discord-btn:hover { + background: rgba(88, 101, 242, 0.35); + border-color: rgba(88, 101, 242, 0.55); + color: #c5cbff; + transform: translateY(-2px); +} + +.discord-mini { + width: 16px; + height: 16px; + flex-shrink: 0; +} + +/* ─── Responsive ─── */ +@media (max-width: 768px) { + .teams-grid { + grid-template-columns: 1fr; + } +}