Compare commits
3 Commits
master
...
web/submit
Author | SHA1 | Date | |
---|---|---|---|
bf0a6c63cd | |||
51d812b3ad | |||
a83ab272bb |
releaser
web/src/app
2
releaser/.cargo/config.toml
Normal file
2
releaser/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[registries.strafesnet]
|
||||||
|
index = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
1
releaser/.gitignore
vendored
Normal file
1
releaser/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1979
releaser/Cargo.lock
generated
Normal file
1979
releaser/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
releaser/Cargo.toml
Normal file
11
releaser/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "releaser"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
submissions-api = { version = "0.1.0", registry = "strafesnet" }
|
||||||
|
rbx_asset = { version = "0.2.5", registry = "strafesnet" }
|
||||||
|
rust-grpc = { version = "1.0.3", registry = "strafesnet" }
|
||||||
|
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }
|
||||||
|
tonic = "0.12.3"
|
24
releaser/Containerfile
Normal file
24
releaser/Containerfile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Using the `rust-musl-builder` as base image, instead of
|
||||||
|
# the official Rust toolchain
|
||||||
|
FROM docker.io/clux/muslrust:stable AS chef
|
||||||
|
USER root
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM chef AS planner
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM chef AS builder
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
|
||||||
|
# Notice that we are specifying the --target flag!
|
||||||
|
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
|
||||||
|
COPY . .
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl --bin releaser
|
||||||
|
|
||||||
|
FROM docker.io/alpine:latest AS runtime
|
||||||
|
RUN addgroup -S myuser && adduser -S myuser -G myuser
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/releaser /usr/local/bin/
|
||||||
|
USER myuser
|
||||||
|
ENTRYPOINT ["/usr/local/bin/releaser"]
|
32
releaser/src/main.rs
Normal file
32
releaser/src/main.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum StartupError{
|
||||||
|
API(submissions_api::ReqwestError),
|
||||||
|
GRPCConnect(tonic::transport::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for StartupError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for StartupError{}
|
||||||
|
|
||||||
|
// annoying mile-long type
|
||||||
|
pub type MapsServiceClient=rust_grpc::maps::maps_service_client::MapsServiceClient<tonic::transport::channel::Channel>;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main()->Result<(),StartupError>{
|
||||||
|
// maps-service api
|
||||||
|
let api_host=std::env::var("API_HOST").expect("API_HOST env required");
|
||||||
|
let api=submissions_api::Context::new(api_host).map_err(StartupError::API)?;
|
||||||
|
|
||||||
|
// data-service grpc for creating map entries
|
||||||
|
let data_host=std::env::var("DATA_HOST").expect("DATA_HOST env required");
|
||||||
|
let maps_grpc=crate::MapsServiceClient::connect(data_host).await.map_err(StartupError::GRPCConnect)?;
|
||||||
|
|
||||||
|
// request maps pending release
|
||||||
|
// randomize list
|
||||||
|
// release maps
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { Button, ButtonOwnProps } from "@mui/material";
|
import { Button, ButtonOwnProps } from "@mui/material";
|
||||||
|
|
||||||
type Review = "Completed" | "Submit" | "Reject" | "Revoke" | "Accept" | "Publish"
|
type Review = "Completed" | "Submit" | "Reject" | "Revoke" | "Accept" | "Validate" | "Upload"
|
||||||
type Action = "completed" | "submit" | "reject" | "revoke" | "trigger-validate" | "trigger-publish"
|
type Action = "completed" | "submit" | "reject" | "revoke" | "trigger-validate" | "trigger-upload"
|
||||||
interface ReviewButton {
|
interface ReviewButton {
|
||||||
name: Review,
|
name: Review,
|
||||||
action: Action,
|
action: Action,
|
||||||
@ -27,8 +27,9 @@ export default function ReviewButtons() {
|
|||||||
<ReviewButton color="info" name="Submit" action="submit"/>
|
<ReviewButton color="info" name="Submit" action="submit"/>
|
||||||
<ReviewButton color="info" name="Revoke" action="revoke"/>
|
<ReviewButton color="info" name="Revoke" action="revoke"/>
|
||||||
<ReviewButton color="info" name="Accept" action="trigger-validate"/>
|
<ReviewButton color="info" name="Accept" action="trigger-validate"/>
|
||||||
|
<ReviewButton color="info" name="Validate" action="trigger-validate"/>
|
||||||
<ReviewButton color="error" name="Reject" action="reject"/>
|
<ReviewButton color="error" name="Reject" action="reject"/>
|
||||||
<ReviewButton color="info" name="Publish" action="trigger-publish"/>
|
<ReviewButton color="info" name="Upload" action="trigger-upload"/>
|
||||||
<ReviewButton color="info" name="Completed" action="completed"/>
|
<ReviewButton color="info" name="Completed" action="completed"/>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
67
web/src/app/submit/_submit.tsx
Normal file
67
web/src/app/submit/_submit.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
interface FormNumbers {
|
||||||
|
readonly GameID: number,
|
||||||
|
readonly AssetID: number,
|
||||||
|
readonly AssetVersion: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormStrings {
|
||||||
|
readonly DisplayName: string,
|
||||||
|
readonly Creator: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
function SubmitAPI(json: string) {
|
||||||
|
console.log(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SafeNumbers(form: FormNumbers): Promise<FormNumbers> {
|
||||||
|
const form_number_values = [form.GameID, form.AssetID, form.AssetVersion]
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
form_number_values.forEach(v => {
|
||||||
|
if (!Number.isSafeInteger(v)) {
|
||||||
|
reject(`Form Value: ${v} was not a valid number`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(form)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function SafeStrings(form: FormStrings): Promise<FormStrings> {
|
||||||
|
const form_string_values = [form.DisplayName, form.Creator]
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
form_string_values.forEach(v => {
|
||||||
|
if (v.length>=128) {
|
||||||
|
reject(`Form value: ${v} is beyond the max 128 string limit`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(form)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Submit() {
|
||||||
|
const form_numbers: FormNumbers = {
|
||||||
|
GameID: Number((document.getElementById("game-id") as HTMLInputElement).value),
|
||||||
|
AssetID: Number((document.getElementById("asset-id") as HTMLInputElement).value),
|
||||||
|
AssetVersion: Number((document.getElementById("asset-version") as HTMLInputElement).value),
|
||||||
|
}
|
||||||
|
const form_strings: FormStrings = {
|
||||||
|
DisplayName: (document.getElementById("display-name") as HTMLInputElement).value,
|
||||||
|
Creator: (document.getElementById("creator") as HTMLInputElement).value
|
||||||
|
}
|
||||||
|
|
||||||
|
const valid_numbers = SafeNumbers(form_numbers)
|
||||||
|
const valid_strings = SafeStrings(form_strings)
|
||||||
|
|
||||||
|
valid_strings.then(form_strings => {
|
||||||
|
valid_numbers.then(form_numbers => {
|
||||||
|
SubmitAPI(JSON.stringify({
|
||||||
|
DisplayName: form_strings.DisplayName,
|
||||||
|
Creator: form_strings.Creator,
|
||||||
|
GameID: form_numbers.GameID,
|
||||||
|
AssetID: form_numbers.AssetID,
|
||||||
|
AssetVersion: form_numbers.AssetVersion
|
||||||
|
}))
|
||||||
|
}).catch(e => console.log(e))
|
||||||
|
}).catch(e => console.log(e))
|
||||||
|
}
|
@ -3,23 +3,19 @@
|
|||||||
import { FormControl, FormLabel, RadioGroup, FormControlLabel, Button, TextField } from "@mui/material"
|
import { FormControl, FormLabel, RadioGroup, FormControlLabel, Button, TextField } from "@mui/material"
|
||||||
import SendIcon from '@mui/icons-material/Send';
|
import SendIcon from '@mui/icons-material/Send';
|
||||||
import Webpage from "@/app/_components/webpage"
|
import Webpage from "@/app/_components/webpage"
|
||||||
|
import Submit from "./_submit";
|
||||||
import Radio from '@mui/material/Radio';
|
import Radio from '@mui/material/Radio';
|
||||||
|
|
||||||
import "./(styles)/page.scss"
|
import "./(styles)/page.scss"
|
||||||
|
|
||||||
const enum Map {
|
|
||||||
New,
|
|
||||||
Fix,
|
|
||||||
}
|
|
||||||
|
|
||||||
function TargetAsset() {
|
function TargetAsset() {
|
||||||
return (<FormControl>
|
return <FormControl>
|
||||||
<FormLabel id="target-asset-radio">Target:</FormLabel>
|
<FormLabel id="target-asset-radio">Target:</FormLabel>
|
||||||
<RadioGroup defaultValue="New" aria-labelledby="target-asset-radio" name="target-asset-radio">
|
<RadioGroup defaultValue="New" aria-labelledby="target-asset-radio" name="target-asset-radio">
|
||||||
<FormControlLabel value="New" control={<Radio/>} label="New"/>
|
<FormControlLabel value="New" control={<Radio/>} label="New" id="asset-new"/>
|
||||||
<FormControlLabel value="Fix" control={<Radio/>} label="Fix"/>
|
<FormControlLabel value="Fix" control={<Radio/>} label="Fix" id="asset-Fix"/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>)
|
</FormControl>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SubmissionInfoPage() {
|
export default function SubmissionInfoPage() {
|
||||||
@ -32,14 +28,14 @@ export default function SubmissionInfoPage() {
|
|||||||
</header>
|
</header>
|
||||||
<form>
|
<form>
|
||||||
<TextField className="form-field" id="display-name" label="Display Name" variant="outlined"/>
|
<TextField className="form-field" id="display-name" label="Display Name" variant="outlined"/>
|
||||||
<TextField className="form-field" id="creator" label="Creator" variant="outlined"/>
|
<TextField className="form-field" id="creator" label="Creator" variant="outlined"/>
|
||||||
<TextField className="form-field" id="game-id" label="Game ID" variant="outlined"/>
|
<TextField className="form-field" id="game-id" label="Game ID" variant="outlined" type="number"/>
|
||||||
<TextField className="form-field" id="asset-id" label="Asset ID" variant="outlined"/>
|
<TextField className="form-field" id="asset-id" label="Asset ID" variant="outlined" type="number"/>
|
||||||
<TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined"/>
|
<TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined" type="number"/>
|
||||||
<TargetAsset/>
|
<TargetAsset/>
|
||||||
</form>
|
</form>
|
||||||
<span className="spacer form-spacer"></span>
|
<span className="spacer form-spacer"></span>
|
||||||
<Button variant="contained" startIcon={<SendIcon/>} sx={{
|
<Button variant="contained" startIcon={<SendIcon />} onClick={() => { Submit() }} sx={{
|
||||||
width: "400px",
|
width: "400px",
|
||||||
height: "50px",
|
height: "50px",
|
||||||
marginInline: "auto"
|
marginInline: "auto"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user