Fixed Formatting of HTML pages, added Prettier Support

This commit is contained in:
Elia Zammuto 2021-08-17 19:12:15 +02:00
parent 62c3faaacb
commit 81317ce672
10 changed files with 1325 additions and 868 deletions

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -14,8 +14,11 @@
<tbody>
<tr v-for="(app,i) in apps" :key="i">
<td>{{app.name}}</td>
<td><button class="btn btn-primary" @click="editApp(i)">Edit</button>
<button class="btn btn-danger" @click="showDeleteForm(i)">Delete</button>
<td>
<button class="btn btn-primary" @click="editApp(i)">Edit</button>
<button class="btn btn-danger" @click="showDeleteForm(i)">
Delete
</button>
</td>
</tr>
</tbody>
@ -26,62 +29,132 @@
<!--name-->
<div class="mb-3">
<label for="appName" class="form-label">Application Name</label>
<input type="text" class="form-control" id="appName" aria-describedby="appNameHelp" v-model="editForm.name">
<div id="appNameHelp" class="form-text">Application Name, as shown on Moonlight</div>
<input
type="text"
class="form-control"
id="appName"
aria-describedby="appNameHelp"
v-model="editForm.name"
/>
<div id="appNameHelp" class="form-text">
Application Name, as shown on Moonlight
</div>
</div>
<!--output-->
<div class="mb-3">
<label for="appOutput" class="form-label">Output</label>
<input type="text" class="form-control monospace" id="appOutput" aria-describedby="appOutputHelp"
v-model="editForm.output">
<div id="appOutputHelp" class="form-text">The file where the output of the command is stored, if it is not
specified, the output is ignored</div>
<input
type="text"
class="form-control monospace"
id="appOutput"
aria-describedby="appOutputHelp"
v-model="editForm.output"
/>
<div id="appOutputHelp" class="form-text">
The file where the output of the command is stored, if it is not
specified, the output is ignored
</div>
</div>
<!--prep-cmd-->
<div class="mb-3 d-flex flex-column">
<label for="appName" class="form-label">Command Preparations</label>
<div class="form-text">A list of commands to be run before/after the application. <br> If any of the
prep-commands fail, starting the application is aborted</div>
<div class="form-text">
A list of commands to be run before/after the application. <br />
If any of the prep-commands fail, starting the application is aborted
</div>
<table v-if="editForm['prep-cmd'].length > 0">
<thead>
<th class="precmd-head">Do</th>
<th class="precmd-head">Undo</th>
<th style="width: 48px;"></th>
<th style="width: 48px"></th>
</thead>
<tbody>
<tr v-for="(c,i) in editForm['prep-cmd']">
<td><input type="text" class="form-control monospace" v-model="c.do"></td>
<td><input type="text" class="form-control monospace" v-model="c.undo"></td>
<td><button class="btn btn-danger" @click="editForm['prep-cmd'].splice(i,1)">&times;</button></td>
<td>
<input
type="text"
class="form-control monospace"
v-model="c.do"
/>
</td>
<td>
<input
type="text"
class="form-control monospace"
v-model="c.undo"
/>
</td>
<td>
<button
class="btn btn-danger"
@click="editForm['prep-cmd'].splice(i,1)"
>
&times;
</button>
</td>
</tr>
</tbody>
</table>
<button class="mt-2 btn btn-success" style="margin: 0 auto;" @click="addPrepCmd">&plus; Add</button>
<button
class="mt-2 btn btn-success"
style="margin: 0 auto"
@click="addPrepCmd"
>
&plus; Add
</button>
</div>
<!--detatched-->
<div class="mb-3">
<label for="appName" class="form-label">Detached Commands</label>
<div v-for="(c,i) in editForm.detached" class="d-flex justify-content-between my-2">
<div
v-for="(c,i) in editForm.detached"
class="d-flex justify-content-between my-2"
>
<pre>{{c}}</pre>
<button class="btn btn-danger mx-2" @click="editForm.detached.splice(i,1)">&times;</button>
<button
class="btn btn-danger mx-2"
@click="editForm.detached.splice(i,1)"
>
&times;
</button>
</div>
<div class="d-flex justify-content-between">
<input type="text" class="form-control monospace" v-model="detachedCmd">
<button class="btn btn-success mx-2" @click="editForm.detached.push(detachedCmd);detachedCmd = '';">+</button>
<input
type="text"
class="form-control monospace"
v-model="detachedCmd"
/>
<button
class="btn btn-success mx-2"
@click="editForm.detached.push(detachedCmd);detachedCmd = '';"
>
+
</button>
</div>
<div class="form-text">
A list of commands to be run and forgotten about
</div>
<div class="form-text">A list of commands to be run and forgotten about</div>
</div>
<!--command-->
<div class="mb-3">
<label for="appCmd" class="form-label">Command</label>
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp"
v-model="editForm.cmd">
<div id="appCmdHelp" class="form-text">The main application, if it is not specified, a processs is started that
sleeps indefinitely</div>
<input
type="text"
class="form-control monospace"
id="appCmd"
aria-describedby="appCmdHelp"
v-model="editForm.cmd"
/>
<div id="appCmdHelp" class="form-text">
The main application, if it is not specified, a processs is started
that sleeps indefinitely
</div>
</div>
<!--buttons-->
<div class="d-flex">
<button @click="showEditForm = false" class="btn btn-secondary m-2">Cancel</button>
<button @click="showEditForm = false" class="btn btn-secondary m-2">
Cancel
</button>
<button class="btn btn-primary m-2" @click="save">Save</button>
</div>
</div>
@ -93,30 +166,32 @@
<script>
new Vue({
el: '#app',
el: "#app",
data() {
return {
apps: [],
showEditForm: false,
editForm: null,
detachedCmd: '',
}
detachedCmd: "",
};
},
created() {
fetch("/api/apps").then(r => r.json()).then((r) => {
fetch("/api/apps")
.then((r) => r.json())
.then((r) => {
console.log(r);
this.apps = r.apps;
})
});
},
methods: {
newApp() {
this.editForm = {
name: '',
output: '',
name: "",
output: "",
cmd: [],
index: -1,
"prep-cmd": [],
"detached": []
detached: [],
};
this.editForm.index = -1;
this.showEditForm = true;
@ -124,12 +199,16 @@
editApp(id) {
this.editForm = JSON.parse(JSON.stringify(this.apps[id]));
this.$set(this.editForm, "index", id);
if (this.editForm["prep-cmd"] === undefined) this.$set(this.editForm, "prep-cmd", []);
if (this.editForm["detached"] === undefined) this.$set(this.editForm, "detached", []);
if (this.editForm["prep-cmd"] === undefined)
this.$set(this.editForm, "prep-cmd", []);
if (this.editForm["detached"] === undefined)
this.$set(this.editForm, "detached", []);
this.showEditForm = true;
},
showDeleteForm(id) {
let resp = confirm("Are you sure to delete " + this.apps[id].name + "?");
let resp = confirm(
"Are you sure to delete " + this.apps[id].name + "?"
);
if (resp) {
fetch("/api/apps/" + id, { method: "DELETE" }).then((r) => {
if (r.status == 200) document.location.reload();
@ -137,18 +216,21 @@
}
},
addPrepCmd() {
this.editForm['prep-cmd'].push({
do: '',
undo: '',
this.editForm["prep-cmd"].push({
do: "",
undo: "",
});
},
save() {
fetch("/api/apps", { method: "POST", body: JSON.stringify(this.editForm) }).then((r) => {
fetch("/api/apps", {
method: "POST",
body: JSON.stringify(this.editForm),
}).then((r) => {
if (r.status == 200) document.location.reload();
});
}
}
})
},
},
});
</script>
<style>

View File

@ -4,8 +4,13 @@
<!--Header-->
<ul class="nav nav-tabs">
<li class="nav-item" v-for="tab in tabs" :key="tab.id">
<a class="nav-link" :class="{'active': tab.id === currentTab}" href="#"
@click="currentTab = tab.id">{{tab.name}}</a>
<a
class="nav-link"
:class="{'active': tab.id === currentTab}"
href="#"
@click="currentTab = tab.id"
>{{tab.name}}</a
>
</li>
</ul>
<!--General Tab-->
@ -13,15 +18,26 @@
<!--Sunshine Name-->
<div class="mb-3">
<label for="sunshine_name" class="form-label">Sunshine Name</label>
<input type="text" class="form-control" id="sunshine_name" placeholder="Sunshine"
v-model="config.sunshine_name">
<div class="form-text">The name displayed by Moonlight. If not specified, the PC's hostname is used
<input
type="text"
class="form-control"
id="sunshine_name"
placeholder="Sunshine"
v-model="config.sunshine_name"
/>
<div class="form-text">
The name displayed by Moonlight. If not specified, the PC's hostname
is used
</div>
</div>
<!--Log Level-->
<div class="mb-3">
<label for="min_log_level" class="form-label">Log Level</label>
<select id="min_log_level" class="form-select" v-model="config.min_log_level">
<select
id="min_log_level"
class="form-select"
v-model="config.min_log_level"
>
<option :value="0">Verbose</option>
<option :value="1">Debug</option>
<option :value="2">Info</option>
@ -30,17 +46,27 @@
<option :value="5">Fatal</option>
<option :value="6">None</option>
</select>
<div class="form-text">The minimum log level printed to standard out</div>
<div class="form-text">
The minimum log level printed to standard out
</div>
</div>
<!--Origin Web UI Allowed-->
<div class="mb-3">
<label for="origin_web_ui_allowed" class="form-label">Origin Web UI Allowed</label>
<select id="origin_web_ui_allowed" class="form-select" v-model="config.origin_web_ui_allowed">
<label for="origin_web_ui_allowed" class="form-label"
>Origin Web UI Allowed</label
>
<select
id="origin_web_ui_allowed"
class="form-select"
v-model="config.origin_web_ui_allowed"
>
<option value="pc">Only localhost may access Web UI</option>
<option value="lan">Only those in LAN may access Web UI</option>
<option value="wan">Anyone may access Web UI</option>
</select>
<div class="form-text">The origin of the remote endpoint address that is not denied access to Web UI
<div class="form-text">
The origin of the remote endpoint address that is not denied access to
Web UI
</div>
</div>
<!--UPnP-->
@ -64,64 +90,124 @@
<!--Ping Timeout-->
<div class="mb-3">
<label for="ping_timeout" class="form-label">Ping Timeout</label>
<input type="text" class="form-control" id="ping_timeout" placeholder="10000"
v-model="config.ping_timeout">
<div class="form-text">How long to wait in milliseconds for data from moonlight before shutting down the
stream</div>
<input
type="text"
class="form-control"
id="ping_timeout"
placeholder="10000"
v-model="config.ping_timeout"
/>
<div class="form-text">
How long to wait in milliseconds for data from moonlight before
shutting down the stream
</div>
</div>
<!--Advertised FPS and Resolutions-->
<div class="mb-3">
<label for="ping_timeout" class="form-label">Advertised Resolutions and FPS</label>
<label for="ping_timeout" class="form-label"
>Advertised Resolutions and FPS</label
>
<div class="resolutions-container">
<label>Resolutions</label>
<div class="resolutions d-flex flex-wrap">
<div class="p-2 ms-item m-2 d-flex justify-content-between" v-for="(r,i) in resolutions"
:key="r">
<div
class="p-2 ms-item m-2 d-flex justify-content-between"
v-for="(r,i) in resolutions"
:key="r"
>
<span class="px-2">{{r}}</span>
<span style="cursor: pointer;" @click="resolutions.splice(i,1)">&times;</span>
<span style="cursor: pointer" @click="resolutions.splice(i,1)"
>&times;</span
>
</div>
<form @submit.prevent="resolutions.push(resIn);resIn = '';" class="d-flex align-items-center">
<input type="text" v-model="resIn" required pattern="[0-9]+x[0-9]+"
style="border-top-right-radius: 0;border-bottom-right-radius: 0;" class="form-control">
<button style="border-top-left-radius: 0;border-bottom-left-radius: 0;"
class="btn btn-success">+</button>
<form
@submit.prevent="resolutions.push(resIn);resIn = '';"
class="d-flex align-items-center"
>
<input
type="text"
v-model="resIn"
required
pattern="[0-9]+x[0-9]+"
style="
border-top-right-radius: 0;
border-bottom-right-radius: 0;
"
class="form-control"
/>
<button
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
class="btn btn-success"
>
+
</button>
</form>
</div>
</div>
<div class="fps-container">
<label>FPS</label>
<div class="fps d-flex flex-wrap">
<div class="p-2 ms-item m-2 d-flex justify-content-between" v-for="(f,i) in fps" :key="f">
<div
class="p-2 ms-item m-2 d-flex justify-content-between"
v-for="(f,i) in fps"
:key="f"
>
<span class="px-2">{{f}}</span>
<span style="cursor: pointer;" @click="fps.splice(i,1)">&times;</span>
<span style="cursor: pointer" @click="fps.splice(i,1)"
>&times;</span
>
</div>
<form @submit.prevent="fps.push(fpsIn);fpsIn = '';" class="d-flex align-items-center">
<input type="text" v-model="fpsIn" required pattern="[0-9]+"
style="width: 6ch;border-top-right-radius: 0;border-bottom-right-radius: 0;"
class="form-control">
<button style="border-top-left-radius: 0;border-bottom-left-radius: 0;"
class="btn btn-success">+</button>
<form
@submit.prevent="fps.push(fpsIn);fpsIn = '';"
class="d-flex align-items-center"
>
<input
type="text"
v-model="fpsIn"
required
pattern="[0-9]+"
style="
width: 6ch;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
"
class="form-control"
/>
<button
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
class="btn btn-success"
>
+
</button>
</form>
</div>
</div>
<div class="form-text">
The display modes advertised by Sunshine<br>
Some versions of Moonlight, such as Moonlight-nx (Switch),
rely on this list to ensure that the requested resolutions and fps
are supported.
The display modes advertised by Sunshine<br />
Some versions of Moonlight, such as Moonlight-nx (Switch), rely on
this list to ensure that the requested resolutions and fps are
supported.
</div>
</div>
<!-- Mapping Key AltRight to Key Windows -->
<div class="mb-3">
<label for="mapkey" class="form-label">Map Right Alt key to Windows key</label>
<select id="mapkey" class="form-select" v-model="config.key_rightalt_to_key_win">
<label for="mapkey" class="form-label"
>Map Right Alt key to Windows key</label
>
<select
id="mapkey"
class="form-select"
v-model="config.key_rightalt_to_key_win"
>
<option value="disabled">Disabled</option>
<option value="enabled">Enabled</option>
</select>
</div>
<div class="form-text">
It may be possible that you cannot send the Windows Key from Moonlight directly.<br />
In those cases it may be usefull to make Sunshine think the Right Alt key is the Windows key
It may be possible that you cannot send the Windows Key from Moonlight
directly.<br />
In those cases it may be usefull to make Sunshine think the Right Alt
key is the Windows key
</div>
</div>
<!--Files Tab-->
@ -129,62 +215,113 @@
<!--Private Key-->
<div class="mb-3">
<label for="pkey" class="form-label">Private Key</label>
<input type="text" class="form-control" id="pkey" placeholder="/dir/pkey.pem" v-model="config.pkey">
<input
type="text"
class="form-control"
id="pkey"
placeholder="/dir/pkey.pem"
v-model="config.pkey"
/>
<div class="form-text">The private key must be 2048 bits</div>
</div>
<!--Cert-->
<div class="mb-3">
<label for="cert" class="form-label">Cert</label>
<input type="text" class="form-control" id="cert" placeholder="/dir/cert.pem" v-model="config.cert">
<div class="form-text">The certificate must be signed with a 2048 bit key</div>
<input
type="text"
class="form-control"
id="cert"
placeholder="/dir/cert.pem"
v-model="config.cert"
/>
<div class="form-text">
The certificate must be signed with a 2048 bit key
</div>
</div>
<!--State File-->
<div class="mb-3">
<label for="file_state" class="form-label">State File</label>
<input type="text" class="form-control" id="file_state" placeholder="sunshine_state.json"
v-model="config.file_state">
<div class="form-text">The file where current state of Sunshine is stored</div>
<input
type="text"
class="form-control"
id="file_state"
placeholder="sunshine_state.json"
v-model="config.file_state"
/>
<div class="form-text">
The file where current state of Sunshine is stored
</div>
</div>
<!--Apps File-->
<div class="mb-3">
<label for="file_apps" class="form-label">Apps File</label>
<input type="text" class="form-control" id="file_apps" placeholder="apps.json"
v-model="config.file_apps">
<div class="form-text">The file where current apps of Sunshine are stored</div>
<input
type="text"
class="form-control"
id="file_apps"
placeholder="apps.json"
v-model="config.file_apps"
/>
<div class="form-text">
The file where current apps of Sunshine are stored
</div>
</div>
</div>
<div v-if="currentTab === 'input'" class="config-page">
<!--Back Button Timeout-->
<div class="mb-3">
<label for="back_button_timeout" class="form-label">Back Button Timeout</label>
<input type="text" class="form-control" id="back_button_timeout" placeholder="2000"
v-model="config.back_button_timeout">
<label for="back_button_timeout" class="form-label"
>Back Button Timeout</label
>
<input
type="text"
class="form-control"
id="back_button_timeout"
placeholder="2000"
v-model="config.back_button_timeout"
/>
<div class="form-text">
The back/select button on the controller.<br>
On the Shield, the home and powerbutton are not passed to Moonlight.<br>
If, after the timeout, the back button is still pressed down, Home/Guide button press is
emulated.<br>
If back_button_timeout &lt; 0, then the Home/Guide button will not be emulated<br>
The back/select button on the controller.<br />
On the Shield, the home and powerbutton are not passed to
Moonlight.<br />
If, after the timeout, the back button is still pressed down,
Home/Guide button press is emulated.<br />
If back_button_timeout &lt; 0, then the Home/Guide button will not be
emulated<br />
</div>
</div>
<!-- Key Repeat Delay-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="key_repeat_delay" class="form-label">Key Repeat Delay</label>
<input type="text" class="form-control" id="key_repeat_delay" placeholder="500"
v-model="config.key_repeat_delay">
<label for="key_repeat_delay" class="form-label"
>Key Repeat Delay</label
>
<input
type="text"
class="form-control"
id="key_repeat_delay"
placeholder="500"
v-model="config.key_repeat_delay"
/>
<div class="form-text">
Control how fast keys will repeat themselves<br>
Control how fast keys will repeat themselves<br />
The initial delay in milliseconds before repeating keys
</div>
</div>
<!-- Key Repeat Frequency-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="key_repeat_frequency" class="form-label">Key Repeat Frequency</label>
<input type="text" class="form-control" id="key_repeat_frequency" placeholder="24.9"
v-model="config.key_repeat_frequency">
<label for="key_repeat_frequency" class="form-label"
>Key Repeat Frequency</label
>
<input
type="text"
class="form-control"
id="key_repeat_frequency"
placeholder="24.9"
v-model="config.key_repeat_frequency"
/>
<div class="form-text">
How often keys repeat every second<br>
How often keys repeat every second<br />
This configurable option supports decimals
</div>
</div>
@ -194,65 +331,99 @@
<!--Audio Sink-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="audio_sink" class="form-label">Audio Sink</label>
<input type="text" class="form-control" id="audio_sink"
placeholder="{0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}" v-model="config.audio_sink">
<input
type="text"
class="form-control"
id="audio_sink"
placeholder="{0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}"
v-model="config.audio_sink"
/>
<div class="form-text">
The name of the audio sink used for Audio Loopback<br>
You can find the name of the audio sink using the following command:<br>
The name of the audio sink used for Audio Loopback<br />
You can find the name of the audio sink using the following
command:<br />
<pre>tools\audio-info.exe</pre>
</div>
</div>
<div class="mb-3" v-if="platform === 'linux'">
<label for="audio_sink" class="form-label">Audio Sink</label>
<input type="text" class="form-control" id="audio_sink"
placeholder="alsa_output.pci-0000_09_00.3.analog-stereo" v-model="config.audio_sink">
<input
type="text"
class="form-control"
id="audio_sink"
placeholder="alsa_output.pci-0000_09_00.3.analog-stereo"
v-model="config.audio_sink"
/>
<div class="form-text">
The name of the audio sink used for Audio Loopback<br>
If you do not specify this variable, pulseaudio will select the default monitor device.<br>
<br>
You can find the name of the audio sink using either command:<br>
The name of the audio sink used for Audio Loopback<br />
If you do not specify this variable, pulseaudio will select the
default monitor device.<br />
<br />
You can find the name of the audio sink using either command:<br />
<pre>pacmd list-sinks | grep "name:"</pre>
<pre>pactl info | grep Source</pre><br>
<pre>pactl info | grep Source</pre>
<br />
</div>
</div>
<!--Virtual Sink-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="virtual_sink" class="form-label">Virtual Sink</label>
<input type="text" class="form-control" id="virtual_sink"
placeholder="{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}" v-model="config.virtual_sink">
<input
type="text"
class="form-control"
id="virtual_sink"
placeholder="{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}"
v-model="config.virtual_sink"
/>
<div class="form-text">
The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows
Sunshine
to stream audio, while muting the speakers.
The virtual sink, is the audio device that's virtual (Like Steam
Streaming Speakers), it allows Sunshine to stream audio, while muting
the speakers.
</div>
</div>
<!--Adapter Name -->
<div class="mb-3" v-if="platform === 'windows'">
<label for="adapter_name" class="form-label">Adapter Name</label>
<input type="text" class="form-control" id="adapter_name" placeholder="Radeon RX 580 Series"
v-model="config.adapter_name">
<input
type="text"
class="form-control"
id="adapter_name"
placeholder="Radeon RX 580 Series"
v-model="config.adapter_name"
/>
<div class="form-text" v-if="platform === 'windows'">
You can select the video card you want to stream:<br>
The appropriate values can be found using the following command:<br>
You can select the video card you want to stream:<br />
The appropriate values can be found using the following command:<br />
<pre>tools\dxgi-info.exe</pre>
</div>
</div>
<!--Output Name -->
<div class="mb-3" class="config-page" v-if="platform === 'windows'">
<label for="output_name" class="form-label">Output Name</label>
<input type="text" class="form-control" id="output_name" placeholder="\\.\DISPLAY1"
v-model="config.output_name">
<input
type="text"
class="form-control"
id="output_name"
placeholder="\\.\DISPLAY1"
v-model="config.output_name"
/>
<div class="form-text">
You can select the video card you want to stream:<br>
The appropriate values can be found using the following command:<br>
tools\dxgi-info.exe<br><br>
You can select the video card you want to stream:<br />
The appropriate values can be found using the following command:<br />
tools\dxgi-info.exe<br /><br />
</div>
</div>
<div class="mb-3" class="config-page" v-if="platform === 'linux'">
<label for="output_name" class="form-label">Monitor number</label>
<input type="text" class="form-control" id="output_name" placeholder="0" v-model="config.output_name">
<input
type="text"
class="form-control"
id="output_name"
placeholder="0"
v-model="config.output_name"
/>
<div class="form-text">
xrandr --listmonitors<br>
xrandr --listmonitors<br />
Example output:
<pre> 0: +HDMI-1 1920/518x1200/324+0+0 HDMI-1</pre>
</div>
@ -262,51 +433,80 @@
<!--Port familly-->
<div class="mb-3">
<label for="port" class="form-label">Port</label>
<input type="number" min="0" max="65529" class="form-control" id="port" placeholder="47989"
v-model="config.port">
<div class="form-text">
Set the familly of ports used by Sunshine
</div>
<input
type="number"
min="0"
max="65529"
class="form-control"
id="port"
placeholder="47989"
v-model="config.port"
/>
<div class="form-text">Set the familly of ports used by Sunshine</div>
</div>
<!-- Quantization Parameter -->
<div class="mb-3">
<label for="qp" class="form-label">Quantitization Parameter</label>
<input type="number" class="form-control" id="qp" placeholder="28" v-model="config.qp">
<input
type="number"
class="form-control"
id="qp"
placeholder="28"
v-model="config.qp"
/>
<div class="form-text">
Quantitization Parameter<br>
Some devices may not support Constant Bit Rate.<br>
For those devices, QP is used instead.<br>
Higher value means more compression, but less quality<br>
Quantitization Parameter<br />
Some devices may not support Constant Bit Rate.<br />
For those devices, QP is used instead.<br />
Higher value means more compression, but less quality<br />
</div>
</div>
<!-- Min Threads -->
<div class="mb-3">
<label for="min_threads" class="form-label">Minimum number of threads used by ffmpeg to encode the
video.</label>
<input type="number" min="1" class="form-control" id="min_threads" placeholder="1"
v-model="config.min_threads">
<label for="min_threads" class="form-label"
>Minimum number of threads used by ffmpeg to encode the video.</label
>
<input
type="number"
min="1"
class="form-control"
id="min_threads"
placeholder="1"
v-model="config.min_threads"
/>
<div class="form-text">
Minimum number of threads used by ffmpeg to encode the video.<br>
Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually<br>
worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest<br>
value that can reliably encode at your desired streaming settings on your hardware.
Minimum number of threads used by ffmpeg to encode the video.<br />
Increasing the value slightly reduces encoding efficiency, but the
tradeoff is usually<br />
worth it to gain the use of more CPU cores for encoding. The ideal
value is the lowest<br />
value that can reliably encode at your desired streaming settings on
your hardware.
</div>
</div>
<!--HEVC Suppport -->
<div class="mb-3">
<label for="hevc_mode" class="form-label">HEVC Support</label>
<select id="hevc_mode" class="form-select" v-model="config.hevc_mode">
<option value="0">Sunshine will specify support for HEVC based on encoder</option>
<option value="1">Sunshine will not advertise support for HEVC</option>
<option value="2">Sunshine will advertise support for HEVC Main profile</option>
<option value="3">Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles
<option value="0">
Sunshine will specify support for HEVC based on encoder
</option>
<option value="1">
Sunshine will not advertise support for HEVC
</option>
<option value="2">
Sunshine will advertise support for HEVC Main profile
</option>
<option value="3">
Sunshine will advertise support for HEVC Main and Main10 (HDR)
profiles
</option>
</select>
<div class="form-text">
Allows the client to request HEVC Main or HEVC Main10 video streams.<br>
HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using
software
encoding.
Allows the client to request HEVC Main or HEVC Main10 video
streams.<br />
HEVC is more CPU-intensive to encode, so enabling this may reduce
performance when using software encoding.
</div>
</div>
<!--Encoder -->
@ -320,75 +520,123 @@
<option value="software">Software</option>
</select>
<div class="form-text">
Force a specific encoder, otherwise Sunshine will use the first encoder that is available
Force a specific encoder, otherwise Sunshine will use the first
encoder that is available
</div>
</div>
<!--FEC Percentage-->
<div class="mb-3">
<label for="fec_percentage" class="form-label">FEC Percentage</label>
<input type="text" class="form-control" id="fec_percentage" placeholder="20"
v-model="config.fec_percentage">
<input
type="text"
class="form-control"
id="fec_percentage"
placeholder="20"
v-model="config.fec_percentage"
/>
<div class="form-text">
Percentage of error correcting packets per data packet in each video frame.<br>
Higher values can correct for more network packet loss, but at the cost of increasing bandwidth
usage.<br>
Percentage of error correcting packets per data packet in each video
frame.<br />
Higher values can correct for more network packet loss, but at the
cost of increasing bandwidth usage.<br />
The default value of 20 is what GeForce Experience uses.
</div>
</div>
<!--Channels-->
<div class="mb-3">
<label for="channels" class="form-label">Channels</label>
<input type="text" class="form-control" id="channels" placeholder="1" v-model="config.channels">
<input
type="text"
class="form-control"
id="channels"
placeholder="1"
v-model="config.channels"
/>
<div class="form-text">
When multicasting, it could be useful to have different configurations for each connected
Client.
For example:
When multicasting, it could be useful to have different configurations
for each connected Client. For example:
<ul>
<li>Clients connected through WAN and LAN have different bitrate contstraints.</li>
<li>
Clients connected through WAN and LAN have different bitrate
contstraints.
</li>
<li>Decoders may require different settings for color</li>
</ul>
Unlike simply broadcasting to multiple Client, this will generate distinct video streams.<br>
Unlike simply broadcasting to multiple Client, this will generate
distinct video streams.<br />
Note, CPU usage increases for each distinct video stream generated
</div>
</div>
<!--Credentials File-->
<div class="mb-3">
<label for="credentials_file" class="form-label">Web Manager Credentials File</label>
<input type="text" class="form-control" id="credentials_file" placeholder="sunshine_state.json"
v-model="config.credentials_file">
<label for="credentials_file" class="form-label"
>Web Manager Credentials File</label
>
<input
type="text"
class="form-control"
id="credentials_file"
placeholder="sunshine_state.json"
v-model="config.credentials_file"
/>
<div class="form-text">
Store Username/Password seperately from Sunshine's state file.
</div>
</div>
<!--Origin PIN Allowed-->
<div class="mb-3">
<label for="origin_pin_allowed" class="form-label">Origin PIN Allowed</label>
<select id="origin_pin_allowed" class="form-select" v-model="config.origin_pin_allowed">
<label for="origin_pin_allowed" class="form-label"
>Origin PIN Allowed</label
>
<select
id="origin_pin_allowed"
class="form-select"
v-model="config.origin_pin_allowed"
>
<option value="pc">Only localhost may access /pin</option>
<option value="lan">Only those in LAN may access /pin</option>
<option value="wan">Anyone may access /pin</option>
</select>
<div class="form-text">The origin of the remote endpoint address that is not denied for HTTP method /pin
<div class="form-text">
The origin of the remote endpoint address that is not denied for HTTP
method /pin
</div>
</div>
<!--External IP-->
<div class="mb-3">
<label for="external_ip" class="form-label">External IP</label>
<input type="text" class="form-control" id="external_ip" placeholder="123.456.789.12"
v-model="config.external_ip">
<div class="form-text">If no external IP address is given, Sunshine will automatically detect external
IP</div>
<input
type="text"
class="form-control"
id="external_ip"
placeholder="123.456.789.12"
v-model="config.external_ip"
/>
<div class="form-text">
If no external IP address is given, Sunshine will automatically detect
external IP
</div>
</div>
</div>
<!--Software Settings-->
<div v-if="currentTab === 'sw'" class="config-page">
<div class="mb-3">
<label for="sw_preset" class="form-label">SW Presets</label>
<input class="form-control" id="sw_preset" placeholder="superfast" v-model="config.sw_preset">
<input
class="form-control"
id="sw_preset"
placeholder="superfast"
v-model="config.sw_preset"
/>
</div>
<div class="mb-3">
<label for="sw_tune" class="form-label">SW Tune</label>
<input class="form-control" id="sw_tune" placeholder="zerolatency" v-model="config.sw_tune">
<input
class="form-control"
id="sw_tune"
placeholder="zerolatency"
v-model="config.sw_tune"
/>
</div>
</div>
<!--Nvidia Encoder Settings-->
@ -419,7 +667,9 @@
<option value="vbr">vbr -- variable bitrate</option>
<option value="cbr">cbr -- constant bitrate</option>
<option value="cbr_hq">cbr_hq -- cbr high quality</option>
<option value="cbr_ld_hq">cbr_ld_hq -- cbr low delay high quality</option>
<option value="cbr_ld_hq">
cbr_ld_hq -- cbr low delay high quality
</option>
<option value="vbr_hq">vbr_hq -- vbr high quality</option>
</select>
</div>
@ -437,7 +687,11 @@
<!--Presets-->
<div class="mb-3">
<label for="amd_quality" class="form-label">AMD AMF Quality</label>
<select id="amd_quality" class="form-select" v-model="config.amd_quality">
<select
id="amd_quality"
class="form-select"
v-model="config.amd_quality"
>
<option value="default">Default</option>
<option value="speed">Speed</option>
<option value="balanced">Balanced</option>
@ -448,8 +702,12 @@
<select id="amd_rc" class="form-select" v-model="config.amd_rc">
<option value="auto">auto -- let ffmpeg decide rate control</option>
<option value="constqp">constqp -- constant QP mode</option>
<option value="vbr_latency">vbr_latency -- Latency Constrained Variable Bitrate</option>
<option value="vbr_peak">vbr_peak -- Peak Contrained Variable Bitrate</option>
<option value="vbr_latency">
vbr_latency -- Latency Constrained Variable Bitrate
</option>
<option value="vbr_peak">
vbr_peak -- Peak Contrained Variable Bitrate
</option>
<option value="cbr">cbr -- constant bitrate</option>
</select>
</div>
@ -463,11 +721,17 @@
</div>
</div>
<div v-if="currentTab === 'va-api'" class="config-page">
<input class="form-control" id="adapter_name" placeholder="/dev/dri/renderD128"
v-model="config.adapter_name">
<input
class="form-control"
id="adapter_name"
placeholder="/dev/dri/renderD128"
v-model="config.adapter_name"
/>
</div>
</div>
<div class="alert alert-success my-4" v-if="success"><b>Success!</b> Restart Sunshine to apply changes</div>
<div class="alert alert-success my-4" v-if="success">
<b>Success!</b> Restart Sunshine to apply changes
</div>
<div class="mb-3 buttons">
<button class="btn btn-primary" @click="save">Save</button>
</div>
@ -475,69 +739,72 @@
<script>
new Vue({
el: '#app',
el: "#app",
data() {
return {
platform: '',
platform: "",
success: false,
config: null,
fps: [],
resolutions: [],
currentTab: 'general',
resIn: '',
fpsIn: '',
tabs: [{
id: 'general',
name: "General"
currentTab: "general",
resIn: "",
fpsIn: "",
tabs: [
{
id: "general",
name: "General",
},
{
id: 'files',
name: "Files"
id: "files",
name: "Files",
},
{
id: 'input',
name: "Input"
id: "input",
name: "Input",
},
{
id: 'av',
name: "Audio/Video"
id: "av",
name: "Audio/Video",
},
{
id: 'advanced',
name: "Advanced"
id: "advanced",
name: "Advanced",
},
{
id: "sw",
name: "Software Encoder"
name: "Software Encoder",
},
{
id: "nv",
name: "NVENC Encoder"
name: "NVENC Encoder",
},
{
id: "amd",
name: "AMF Encoder"
name: "AMF Encoder",
},
{
id: "va-api",
name: "VA-API encoder"
}
]
}
name: "VA-API encoder",
},
],
};
},
created() {
fetch("/api/config").then(r => r.json()).then((r) => {
fetch("/api/config")
.then((r) => r.json())
.then((r) => {
this.config = r;
this.platform = this.config.platform;
var app = document.getElementById("app");
if (this.platform == "windows") {
this.tabs = this.tabs.filter(el => {
this.tabs = this.tabs.filter((el) => {
return el.id !== "va-api";
});
}
if (this.platform == "linux") {
this.tabs = this.tabs.filter(el => {
this.tabs = this.tabs.filter((el) => {
return el.id !== "amd";
});
}
@ -545,45 +812,59 @@
delete this.config.status;
delete this.config.platform;
//Populate default values if not present in config
this.config.key_rightalt_to_key_win = this.config.key_rightalt_to_key_win || "disabled";
this.config.gamepad = this.config.gamepad || 'x360';
this.config.upnp = this.config.upnp || 'disabled';
this.config.key_rightalt_to_key_win =
this.config.key_rightalt_to_key_win || "disabled";
this.config.gamepad = this.config.gamepad || "x360";
this.config.upnp = this.config.upnp || "disabled";
this.config.min_log_level = this.config.min_log_level || 2;
this.config.origin_pin_allowed = this.config.origin_pin_allowed || "pc";
this.config.origin_web_ui_allowed = this.config.origin_web_manager_allowed || "lan";
this.config.origin_pin_allowed =
this.config.origin_pin_allowed || "pc";
this.config.origin_web_ui_allowed =
this.config.origin_web_manager_allowed || "lan";
this.config.hevc_mode = this.config.hevc_mode || 0;
this.config.encoder = this.config.encoder || '';
this.config.nv_preset = this.config.nv_preset || 'default';
this.config.nv_rc = this.config.nv_rc || 'auto';
this.config.nv_coder = this.config.nv_coder || 'auto';
this.config.amd_quality = this.config.amd_quality || 'default';
this.config.amd_rc = this.config.amd_rc || 'auto';
this.config.fps = this.config.fps || '[10, 30, 60, 90, 120]';
this.config.resolutions = this.config.resolutions || '[352x240,480x360,858x480,1280x720,1920x1080,2560x1080,3440x1440,1920x1200,3860x2160,3840x1600]';
this.config.encoder = this.config.encoder || "";
this.config.nv_preset = this.config.nv_preset || "default";
this.config.nv_rc = this.config.nv_rc || "auto";
this.config.nv_coder = this.config.nv_coder || "auto";
this.config.amd_quality = this.config.amd_quality || "default";
this.config.amd_rc = this.config.amd_rc || "auto";
this.config.fps = this.config.fps || "[10, 30, 60, 90, 120]";
this.config.resolutions =
this.config.resolutions ||
"[352x240,480x360,858x480,1280x720,1920x1080,2560x1080,3440x1440,1920x1200,3860x2160,3840x1600]";
this.fps = JSON.parse(this.config.fps);
//Resolutions should be fixed because are not valid JSON
let res = this.config.resolutions.substring(1, this.config.resolutions.length - 1);
let res = this.config.resolutions.substring(
1,
this.config.resolutions.length - 1
);
let resolutions = [];
res.split(",").forEach(r => resolutions.push(r.trim()));
res.split(",").forEach((r) => resolutions.push(r.trim()));
this.resolutions = resolutions;
})
});
},
methods: {
save() {
this.success = false;
let nl = this.config === 'windows' ? "\r\n" : "\n";
this.config.resolutions = "[" + nl + " " + this.resolutions.join("," + nl + " ") + nl + "]";
let nl = this.config === "windows" ? "\r\n" : "\n";
this.config.resolutions =
"[" +
nl +
" " +
this.resolutions.join("," + nl + " ") +
nl +
"]";
this.config.fps = JSON.stringify(this.fps);
fetch("/api/config", {
method: "POST",
body: JSON.stringify(this.config)
body: JSON.stringify(this.config),
}).then((r) => {
if (r.status == 200) this.success = true;
});
}
}
})
},
},
});
</script>
<style>
@ -598,7 +879,7 @@
}
.ms-item {
background-color: #CCC;
background-color: #ccc;
font-size: 12px;
font-weight: bold;
}

View File

@ -1,17 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sunshine</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous">
</script>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8"
crossorigin="anonymous"
></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
</head>
<body>
<body></body>
</html>

View File

@ -1,26 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sunshine</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8" crossorigin="anonymous">
</script>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-p34f1UUtsS3wqzfto5wAAmdvj+osOnFyQFpp4Ua3gs/ZVWx6oOypYoCJhGGScy+8"
crossorigin="anonymous"
></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #ffc400;">
<nav
class="navbar navbar-expand-lg navbar-light"
style="background-color: #ffc400"
>
<div class="container-fluid">
<span class="navbar-brand">Sunshine</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -42,4 +56,7 @@
</li>
</ul>
</div>
</div>
</nav>
</body>
</html>

View File

@ -1,9 +1,11 @@
<div id="content" class="container">
<div class="row">
<div class="col-md-6 py-4" style="margin: 0 auto;">
<div class="col-md-6 py-4" style="margin: 0 auto">
<h1>Hello, Sunshine!</h1>
<p>Sunshine is a Gamestream host for Moonlight</p>
<a href="https://github.com/loki-47-6F-64/sunshine">Official GitHub Repository</a>
<a href="https://github.com/loki-47-6F-64/sunshine"
>Official GitHub Repository</a
>
</div>
</div>
</div>

View File

@ -6,34 +6,71 @@
<h4>Current Credentials</h4>
<div class="mb-3">
<label for="currentUsername" class="form-label">Username</label>
<input required type="text" class="form-control" id="currentUsername" v-model="passwordData.currentUsername">
<input
required
type="text"
class="form-control"
id="currentUsername"
v-model="passwordData.currentUsername"
/>
<div class="form-text">&nbsp;</div>
</div>
<div class="mb-3">
<label for="currentPassword" class="form-label">Password</label>
<input autocomplete="current-password" type="password" class="form-control" id="currentPassword" v-model="passwordData.currentPassword">
<input
autocomplete="current-password"
type="password"
class="form-control"
id="currentPassword"
v-model="passwordData.currentPassword"
/>
</div>
</div>
<div class="col-md-6 px-4">
<h4>New Credentials</h4>
<div class="mb-3">
<label for="newUsername" class="form-label">New Username</label>
<input type="text" class="form-control" id="newUsername" v-model="passwordData.newUsername">
<div class="form-text">If not specified, the username will not change
<input
type="text"
class="form-control"
id="newUsername"
v-model="passwordData.newUsername"
/>
<div class="form-text">
If not specified, the username will not change
</div>
</div>
<div class="mb-3">
<label for="newPassword" class="form-label">Password</label>
<input autocomplete="new-password" required type="password" class="form-control" id="newPassword" v-model="passwordData.newPassword">
<input
autocomplete="new-password"
required
type="password"
class="form-control"
id="newPassword"
v-model="passwordData.newPassword"
/>
</div>
<div class="mb-3">
<label for="confirmNewPassword" class="form-label">Confirm Password</label>
<input autocomplete="new-password" required type="password" class="form-control" id="confirmNewPassword" v-model="passwordData.confirmNewPassword">
<label for="confirmNewPassword" class="form-label"
>Confirm Password</label
>
<input
autocomplete="new-password"
required
type="password"
class="form-control"
id="confirmNewPassword"
v-model="passwordData.confirmNewPassword"
/>
</div>
</div>
</div>
<div class="alert alert-danger" v-if="error"><b>Error: </b>{{error}}</div>
<div class="alert alert-success" v-if="success"><b>Success! </b>This page will reload soon, your browser will ask you for the new credentials</div>
<div class="alert alert-success" v-if="success">
<b>Success! </b>This page will reload soon, your browser will ask you for
the new credentials
</div>
<div class="mb-3 buttons">
<button class="btn btn-primary">Save</button>
</div>
@ -42,26 +79,26 @@
<script>
new Vue({
el: '#app',
el: "#app",
data() {
return {
error: null,
success: false,
passwordData: {
currentUsername: '',
currentPassword: '',
newUsername: '',
newPassword: '',
confirmNewPassword: ''
}
}
currentUsername: "",
currentPassword: "",
newUsername: "",
newPassword: "",
confirmNewPassword: "",
},
};
},
methods: {
save() {
this.error = null;
fetch("/api/password", {
method: "POST",
body: JSON.stringify(this.passwordData)
body: JSON.stringify(this.passwordData),
}).then((r) => {
if (r.status == 200) {
r.json().then((rj) => {
@ -73,15 +110,14 @@
} else {
this.error = rj.error;
}
})
}
else {
this.error = "Internal Server Error"
});
} else {
this.error = "Internal Server Error";
}
});
}
}
})
},
},
});
</script>
<style>

View File

@ -1,13 +1,18 @@
<div id="content" class="container">
<h1 class="my-4">PIN Pairing</h1>
<form action="" class="form d-flex flex-column align-items-center" id="form">
<div class="card flex-column d-flex p-4 mb-4">
<input type="number" placeholder="PIN" id="pin-input" class="form-control my-4">
<input
type="number"
placeholder="PIN"
id="pin-input"
class="form-control my-4"
/>
<button class="btn btn-primary">Send</button>
</div>
<div class="alert alert-warning">
<b>Warning!</b> Make sure you have access to the client you are pairing with.<br>
<b>Warning!</b> Make sure you have access to the client you are pairing
with.<br />
This software can give total control to your computer, so be careful!
</div>
<div id="status"></div>
@ -20,12 +25,18 @@
let pin = document.querySelector("#pin-input").value;
document.querySelector("#status").innerHTML = "";
let b = JSON.stringify({ pin: pin });
fetch("/api/pin",{method: "POST",body: b}).then((response) => response.json()).then((response)=>{
fetch("/api/pin", { method: "POST", body: b })
.then((response) => response.json())
.then((response) => {
if (response.status) {
document.querySelector("#status").innerHTML = `<div class="alert alert-success" role="alert">Success! Please check Moonlight to continue</div>`;
document.querySelector(
"#status"
).innerHTML = `<div class="alert alert-success" role="alert">Success! Please check Moonlight to continue</div>`;
} else {
document.querySelector("#status").innerHTML = `<div class="alert alert-danger" role="alert">PIN does not match, please check if it's typed correctly</div>`;
document.querySelector(
"#status"
).innerHTML = `<div class="alert alert-danger" role="alert">PIN does not match, please check if it's typed correctly</div>`;
}
})
})
});
});
</script>

View File

@ -1,51 +1,73 @@
<main role="main" id="app" style="max-width: 600px;margin: 0 auto;">
<main role="main" id="app" style="max-width: 600px; margin: 0 auto">
<div class="container-parent">
<div class="container py-3">
<h1 class="mb-0">Welcome to Sunshine!</h1>
<p class="mb-0 align-self-start">Before Getting Started, write down below these credentials</p>
<p class="mb-0 align-self-start">
Before Getting Started, write down below these credentials
</p>
</div>
</div>
<div class="alert alert-warning">These Credentials down below are needed to access the rest of the application.<br> Keep them safe, since <b>you will never see them again!</b></div>
<form @submit.prevent="save" class="card p-4" style="width: 100%;">
<div class="alert alert-warning">
These Credentials down below are needed to access the rest of the
application.<br />
Keep them safe, since <b>you will never see them again!</b>
</div>
<form @submit.prevent="save" class="card p-4" style="width: 100%">
<div class="mb-2">
<label for="" class="form-label">Username: </label>
<input type="text" class="form-control" v-model="passwordData.newUsername">
<input
type="text"
class="form-control"
v-model="passwordData.newUsername"
/>
</div>
<div class="mb-2">
<label for="" class="form-label">Password: </label>
<input type="password" class="form-control" v-model="passwordData.newPassword" required>
<input
type="password"
class="form-control"
v-model="passwordData.newPassword"
required
/>
</div>
<div class="mb-2">
<label for="" class="form-label">Password: </label>
<input type="password" class="form-control" v-model="passwordData.confirmNewPassword" required>
<input
type="password"
class="form-control"
v-model="passwordData.confirmNewPassword"
required
/>
</div>
<button class="mb-2 btn btn-primary" style="margin: 1em auto;">Login</button>
<button class="mb-2 btn btn-primary" style="margin: 1em auto">Login</button>
<div class="alert alert-danger" v-if="error"><b>Error: </b>{{error}}</div>
<div class="alert alert-success" v-if="success"><b>Success! </b>This page will reload soon, your browser will ask you for the new credentials</div>
</form>
<div class="alert alert-success" v-if="success">
<b>Success! </b>This page will reload soon, your browser will ask you for
the new credentials
</div>
</form>
</main>
<script>
new Vue({
el: '#app',
el: "#app",
data() {
return {
error: null,
success: false,
passwordData: {
newUsername: 'sunshine',
newPassword: '',
confirmNewPassword: ''
}
}
newUsername: "sunshine",
newPassword: "",
confirmNewPassword: "",
},
};
},
methods: {
save() {
this.error = null;
fetch("/api/password", {
method: "POST",
body: JSON.stringify(this.passwordData)
body: JSON.stringify(this.passwordData),
}).then((r) => {
if (r.status == 200) {
r.json().then((rj) => {
@ -57,13 +79,12 @@
} else {
this.error = rj.error;
}
})
}
else {
this.error = "Internal Server Error"
});
} else {
this.error = "Internal Server Error";
}
});
}
}
})
},
},
});
</script>