Compare commits
26 Commits
break-data
...
main
Author | SHA1 | Date |
---|---|---|
~erin | cd2e47a852 | |
Luna | 2161bc6d5d | |
Luna | 62614bf461 | |
Luna | cbe1b254cc | |
Luna | 34326c00eb | |
Luna | eb6bec830e | |
~erin | 1de762f788 | |
~erin | da51a37b49 | |
~erin | b3fa390c72 | |
Luna | adb6d054dd | |
~erin | ebcda9b067 | |
~erin | b099448006 | |
~erin | ba90ca471b | |
~erin | b4e537b8df | |
~erin | c665951b9d | |
~erin | 7963ce9199 | |
~erin | 625485674e | |
Luna | ccd7a2c267 | |
Luna | 356524d35e | |
Luna | df873ec77a | |
Luna | d88f243ee0 | |
Luna | 971e0e338f | |
Luna | 1b8694ef8d | |
~erin | 475729b1a0 | |
~erin | e5384ac588 | |
Luna | e73108f830 |
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -3,16 +3,10 @@
|
|||
- `/api/register` & `/api/login` now use JSON data
|
||||
- Changing user info now uses Enum (Name, Pin, or Pronouns)
|
||||
|
||||
### 0.5.2
|
||||
- When changing a username, it now deletes the previous user instead of creating a new one
|
||||
- Database functions should now use some error handling
|
||||
- Functions use `db_read_user()` instead of reading in the whole database
|
||||
|
||||
### 0.5.1
|
||||
- `/api/logout` API to delete session token
|
||||
- Add basic support for different message types
|
||||
- Messages now use unix timestamps
|
||||
- Backend finds timestamp
|
||||
### 0.6.1
|
||||
- Add basic support for different user roles & moderator commands
|
||||
- Any user names "admin" will be made into an Admin user
|
||||
- User pronouns are stored in message body now
|
||||
|
||||
## 0.5.0
|
||||
- Most actions should now fail on a NULL token
|
||||
|
@ -20,6 +14,17 @@
|
|||
- Use sled database instead of json file to store users
|
||||
- Now use `GET /api/token/<name>` to validate a users session token
|
||||
|
||||
### 0.5.1
|
||||
- `/api/logout` API to delete session token
|
||||
- Add basic support for different message types
|
||||
- Messages now use unix timestamps
|
||||
- Backend finds timestamp
|
||||
|
||||
### 0.5.2
|
||||
- When changing a username, it now deletes the previous user instead of creating a new one
|
||||
- Database functions should now use some error handling
|
||||
- Functions use `db_read_user()` instead of reading in the whole database
|
||||
|
||||
## 0.4.0
|
||||
- Serve frontend code
|
||||
- Set cookie for token
|
||||
|
|
|
@ -576,6 +576,25 @@ version = "0.2.98"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "lila-chat"
|
||||
version = "0.6.1"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"log 0.4.14",
|
||||
"once_cell",
|
||||
"random-string",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sled",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.4"
|
||||
|
@ -805,25 +824,6 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pogchat"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"log 0.4.14",
|
||||
"once_cell",
|
||||
"random-string",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sled",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.4.5"
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,7 +1,11 @@
|
|||
[package]
|
||||
name = "pogchat"
|
||||
version = "0.6.0"
|
||||
authors = ["Erin Nova <erin@the-system.eu.org>"]
|
||||
name = "lila-chat"
|
||||
description = "A basic chat server written in rust"
|
||||
version = "0.6.1"
|
||||
documentation = "https://git.lavender.software/erin/lila-chat/wiki"
|
||||
repository = "https://git.lavender.software/erin/lila-chat/"
|
||||
license = "CNPLv6+"
|
||||
authors = ["Erin Nova <erin@the-system.eu.org>", "Charlotte"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
109
README.md
109
README.md
|
@ -4,101 +4,7 @@ A simple chat system for built for maya's livestream.
|
|||
Provides a simple API for user authentication, and chat functions.
|
||||
Frontend & backend code stored here.
|
||||
|
||||
## Auth API Documentation
|
||||
|
||||
Most API functions will return JSON in the following format:
|
||||
|
||||
`status`: either `ok` if action succeeded, or `fail` otherwise.
|
||||
|
||||
`reason`: More info about why the action failed.
|
||||
|
||||
### Register & Login:
|
||||
|
||||
`POST /api/register` with JSON body values of: `name`, `pin`, `pronouns`.
|
||||
|
||||
Will return JSON with `status` and `reason`.
|
||||
|
||||
`POST /api/login` with JSON body values of: `name`, `pin`.
|
||||
|
||||
Will return JSON with `status` and `reason`.
|
||||
|
||||
Will set a private cookie named `token` which is used for authentication.
|
||||
|
||||
### Change User Information
|
||||
|
||||
User information such as name, pin, and pronouns, can be changed currently one at a time.
|
||||
|
||||
`POST /api/change` with JSON body values of: `name`, `changed_event`, `new_event`.
|
||||
|
||||
`name` the user's current username. used for authentication.
|
||||
|
||||
`changed_event` which event to change. value can be one of: `Name`, `Pin`, `Pronouns`.
|
||||
|
||||
`new_event` the new value for the changed event.
|
||||
|
||||
User is authenticated via token.
|
||||
|
||||
### Check if User is Still Logged in
|
||||
|
||||
Instead of having to save the pin and re-login every time to check wether they're logged in, you can just check via the token.
|
||||
|
||||
`GET /api/token/<name>` where `<name>` is the current username.
|
||||
|
||||
Will return JSON with `status` and `reason`.
|
||||
|
||||
### Logout
|
||||
|
||||
This API will remove the cookie from the client, as well as invalidating the token serverside.
|
||||
|
||||
`POST /api/logout` with JSON body values of: `name`.
|
||||
|
||||
Will use the current token as authentication.
|
||||
|
||||
Will return JSON with `status` and `reason`.
|
||||
|
||||
### Get Info About A User
|
||||
|
||||
This API will return info about a user on success.
|
||||
|
||||
`GET /api/users/<name>`
|
||||
|
||||
On success returns JSON in format:
|
||||
```
|
||||
status: ok
|
||||
user:
|
||||
name: user's name
|
||||
pronouns: user's pronouns
|
||||
role: the users role, one of either 'Normal', 'Moderator', or 'Admin'
|
||||
```
|
||||
|
||||
eg:
|
||||
|
||||
```
|
||||
{
|
||||
status: "ok",
|
||||
user: {
|
||||
name: "example",
|
||||
pronouns: "they/them",
|
||||
role: "Normal",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Chat API Documentation
|
||||
|
||||
`POST /api/message/send {"name":"username","body":"message body"}` Post a message with JSON body values of: `name` & `body`
|
||||
|
||||
Will return JSON with `status` and `reason`.
|
||||
|
||||
`GET /api/message/messages.json` Returns a json file of all the messages
|
||||
|
||||
## Chat Planning
|
||||
|
||||
Clientside js will register & check login of users, if login is correct will ask for a random token.
|
||||
Backend will generate token, store it, and then send it to the client to set as a cookie.
|
||||
Whenever user sends a message, client will send message & token and backend will check if token matches.
|
||||
|
||||
## To-Do:
|
||||
## Implemented Features:
|
||||
|
||||
- [x] Basic auth API
|
||||
- [x] Return json instead of string
|
||||
|
@ -108,7 +14,6 @@ Whenever user sends a message, client will send message & token and backend will
|
|||
- [x] Finish up `chat::create_message()`
|
||||
- [x] Create `chat::fetch_messages()`
|
||||
- [x] Use unix timestamp for date
|
||||
- [ ] Create `chat::delete_message()`
|
||||
- [x] Switch to using sled database to store users
|
||||
- [x] Error handling
|
||||
- [x] Token generation & storage
|
||||
|
@ -123,10 +28,20 @@ Whenever user sends a message, client will send message & token and backend will
|
|||
- [x] Set pronouns
|
||||
- [x] Change pronouns
|
||||
- [x] make changed_event Enum, use token instead of pin
|
||||
|
||||
## To-Do:
|
||||
|
||||
- [x] Better messaging system
|
||||
- [ ] Use websockets for chat [issue#3](https://git.lavender.software/erin/lila-chat/issues/3)
|
||||
- [ ] Create `chat::delete_message()`
|
||||
- [ ] Display pronouns [issue#4](https://git.lavender.software/erin/lila-chat/issues/4)
|
||||
- [ ] Various database improvements [issue#1](https://git.lavender.software/erin/lila-chat/issues/1)
|
||||
- [ ] Improve error handling on write functions
|
||||
- [ ] Allow for asyncronous reading/writing
|
||||
- [ ] Some form of plural support?
|
||||
- [ ] User management (banning, etc.)
|
||||
- [x] User roles (admin, mod, etc.)
|
||||
- [ ] Commands to affect users
|
||||
- [ ] Commands to affect users [issue#2](https://git.lavender.software/erin/lila-chat/issues/2)
|
||||
- [ ] Blacklist words from chat/names
|
||||
- [ ] More advanced chat features
|
||||
- [x] Different types of message events? eg. default, announcement, command
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -9,9 +9,6 @@
|
|||
id="svg5"
|
||||
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:export-filename="/home/erin/bitmap.png"
|
||||
inkscape:export-xdpi="200"
|
||||
inkscape:export-ydpi="200"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -1,43 +0,0 @@
|
|||
html {
|
||||
background: #F7A8B8;
|
||||
text-align: center;
|
||||
font-family: "Lucida Console", "Courier New", monospace;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 3%;
|
||||
}
|
||||
|
||||
label {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
background-color: white;
|
||||
padding: 1rem;
|
||||
box-shadow: 10px 10px 10px black;
|
||||
width: 40rem;
|
||||
height: 40rem;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#innerchatbox {
|
||||
background-color: rgba(60, 60, 60, .75);
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
overflow-y: scroll;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#loggeduser {
|
||||
padding-top: 1%
|
||||
}
|
||||
|
||||
#error {
|
||||
padding-top: 2.5%;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// VARIABLES
|
||||
let date = '2021-07-22'
|
||||
let messageCount = 0;
|
||||
let username = localStorage.getItem('username');
|
||||
const form = document.querySelector('form');
|
||||
|
||||
// SEND A MESSAGE
|
||||
|
||||
// GRABS MESSAGE FROM FORM & SENDS
|
||||
form.addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(form);
|
||||
|
||||
formMessage = formData.get('message');
|
||||
|
||||
sendMessage()
|
||||
|
||||
})
|
||||
|
||||
//SEND MESSAGE FETCH FUNCTION
|
||||
|
||||
async function sendMessage() {
|
||||
sendMessageInfo = { "name": username, "body": formMessage, "date": date }
|
||||
fetch('/api/message/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendMessageInfo),
|
||||
})
|
||||
form.reset()
|
||||
}
|
||||
|
||||
|
||||
// RECIEVE MESSAGES
|
||||
|
||||
let messageUpdate = window.setInterval(fetchMessages, 500);
|
||||
|
||||
async function fetchMessages() {
|
||||
const response = await fetch('/api/message/messages.json');
|
||||
const recievedMessages = await response.json();
|
||||
document.getElementById("innerchatbox").innerHTML = ""
|
||||
|
||||
for (const message of recievedMessages) {
|
||||
printText(message.user.bold() + ": " + message.body);
|
||||
}
|
||||
|
||||
|
||||
if (recievedMessages.length != messageCount) {
|
||||
let scroll = document.getElementById("innerchatbox");
|
||||
scroll.scrollTop = scroll.scrollHeight;
|
||||
}
|
||||
|
||||
messageCount = recievedMessages.length;
|
||||
}
|
||||
|
||||
|
||||
// FUNCTION TO PRINT MESSAGES IN THE CHAT BOX
|
||||
|
||||
function printText(text) {
|
||||
let p = document.createElement("p");
|
||||
const div = document.getElementById("innerchatbox");
|
||||
div.appendChild(p)
|
||||
p.innerHTML = text
|
||||
}
|
||||
|
||||
|
||||
//LOGGED IN STUFF
|
||||
//TODO ADD CHECK TO SEE IF USERNAME AND TOKEN MATCHES
|
||||
if (username === null) {
|
||||
document.querySelector("#loggeduser").innerHTML = 'You are not logged in'
|
||||
username = ''
|
||||
} else {
|
||||
document.querySelector("#loggeduser").innerHTML = `You are logged in as ${username}`
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
html {
|
||||
background: #F7A8B8;
|
||||
text-align: center;
|
||||
font-family: "Linotte", "Lucida Console", monospace;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Linotte;
|
||||
src: url(../assets/LinotteRegular.otf);
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-top: 1%;
|
||||
padding: 2%;
|
||||
width: 100%;
|
||||
background-color: darkgrey;
|
||||
border-style: none;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: black;
|
||||
border-bottom-width: 1px;
|
||||
font-family: "Lucida Console", "Courier New", monospace;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-top: 1%;
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
#loggeduser {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
#outerchatbox {
|
||||
background-color: rgb(255, 199, 207);
|
||||
padding: 1rem;
|
||||
box-shadow: 10px 10px 10px black;
|
||||
width: 30rem;
|
||||
height: 40rem;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
background-color: rgb(60, 60, 60);
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
text-align: left;
|
||||
color: white;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#loggeduser {
|
||||
padding-top: 2%
|
||||
}
|
||||
|
||||
#errormessage {
|
||||
padding-top: 2.5%;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
html {
|
||||
background: #F7A8B8;
|
||||
font-family: "Linotte", "Lucida Console", monospace;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Linotte;
|
||||
src: url(../assets/LinotteRegular.otf);
|
||||
}
|
||||
|
||||
a {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#streamchat {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
margin-top: -1%;
|
||||
|
||||
}
|
||||
|
||||
#stream {
|
||||
width: 960px;
|
||||
height: 540px;
|
||||
margin-right: 0.3em;
|
||||
margin-left: -40%;
|
||||
margin-top: 5%;
|
||||
margin-bottom: 20%;
|
||||
font-size: 250%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#streamframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
flex: 1;
|
||||
height: 53rem;
|
||||
margin-left: 0.3em;
|
||||
margin-right: -30%;
|
||||
margin-top: -8%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.navigation-list {
|
||||
display: inline-flex;
|
||||
list-style-type: none;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 150px;
|
||||
background-color: #55CDFC;
|
||||
margin: auto;
|
||||
margin-bottom: 2%;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
opacity: 0.8;
|
||||
font-size: larger;
|
||||
color: black;
|
||||
padding: 15px;
|
||||
border-bottom: solid;
|
||||
border-left: solid;
|
||||
border-color: lightblue;
|
||||
border-width: 2px;
|
||||
}
|
|
@ -13,7 +13,7 @@ form {
|
|||
}
|
||||
|
||||
#box {
|
||||
background-color: white;
|
||||
background-color: rgb(255, 199, 207);
|
||||
padding: 5%;
|
||||
box-shadow: 10px 10px 10px black;
|
||||
border-radius: 5px;
|
||||
|
@ -25,6 +25,10 @@ form {
|
|||
padding-top: 1%;
|
||||
}
|
||||
|
||||
#logoutlink {
|
||||
display: block;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: "Lucida Console", "Courier New", monospace;
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="description" content="Maya's Stream Chat">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="chat.css">
|
||||
<link rel="stylesheet" href="../css/chat.css">
|
||||
<link rel="icon" href="">
|
||||
</head>
|
||||
|
||||
|
@ -15,22 +15,27 @@
|
|||
|
||||
<h1>Welcome to the chat!</h1>
|
||||
|
||||
<div id="chatbox">
|
||||
<div id="outerchatbox">
|
||||
|
||||
<div id="innerchatbox">
|
||||
<div id="chatbox">
|
||||
|
||||
</div>
|
||||
|
||||
<form autocomplete="off">
|
||||
<label for="message">Input Message Here:</label>
|
||||
<input type="text" id="message" name="message" required>
|
||||
<label for="message">Input Message Above</label>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="loggeduser"></div>
|
||||
|
||||
<script src="chat.js"></script>
|
||||
<button type="button" id="logoutbutton" onclick="logout()">Logout</button>
|
||||
|
||||
<div id="errormessage"></div>
|
||||
|
||||
<script src="../js/chat.js"></script>
|
||||
<script src="../js/logout.js"></script>
|
||||
|
||||
</body>
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
<meta name="description" content="Chat Login">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<link rel="icon" href="../assets/favicon.svg">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -27,7 +27,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<script src="login.js"></script>
|
||||
<script src="../js/login.js"></script>
|
||||
|
||||
<div id="errormessage"></div>
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
<meta name="description" content="Chat Register">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<link rel="icon" href="../assets/favicon.svg">
|
||||
</head>
|
||||
|
||||
<h1>Register:</h1>
|
||||
|
@ -24,12 +24,12 @@
|
|||
|
||||
<label for="selected">Pronouns:</label><br>
|
||||
<select id="selected" name="selected">
|
||||
<option value="nothing"></option>
|
||||
<option value="she.her">she/her</option>
|
||||
<option value="he.him">he/him</option>
|
||||
<option value="they.them">they/them</option>
|
||||
<option value="it.its">it/its</option>
|
||||
<option value="fae.faer">fae/faer</option>
|
||||
<option value="none"></option>
|
||||
<option value="she/her">she/her</option>
|
||||
<option value="he/him">he/him</option>
|
||||
<option value="they/them">they/them</option>
|
||||
<option value="it/its">it/its</option>
|
||||
<option value="fae/faer">fae/faer</option>
|
||||
</select>
|
||||
|
||||
<p>Or.</p>
|
||||
|
@ -45,7 +45,7 @@
|
|||
|
||||
<div id="errormessage"></div>
|
||||
|
||||
<script src="register.js"></script>
|
||||
<script src="../js/register.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -7,8 +7,8 @@
|
|||
<meta name="description" content="Chat Login Change">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<link rel="icon" href="../assets/favicon.svg">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -53,7 +53,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<script src="updateinfo.js"></script>
|
||||
<script src="../js/updateinfo.js"></script>
|
||||
|
||||
<div id="errormessage"></div>
|
||||
</body>
|
|
@ -1,34 +1,73 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Maya's Stream</title>
|
||||
<meta name="author" content="Luna">
|
||||
<meta name="description" content="Maya's Stream">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="stream.css">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
</head>
|
||||
<head>
|
||||
<title>Maya's Stream</title>
|
||||
<meta name="author" content="Luna">
|
||||
<meta name="description" content="Maya's Stream">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="css/index.css">
|
||||
<link rel="icon" href="assets/favicon.svg">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<body>
|
||||
<nav>
|
||||
|
||||
<a href="register.html">Register Page</a>
|
||||
<ul class="navigation-list">
|
||||
<li><a href="index.html">
|
||||
<div class="buttons">Stream</div>
|
||||
</a></li>
|
||||
<li><a href="/html/chat.html">
|
||||
<div class="buttons">Just Chat</div>
|
||||
</a></li>
|
||||
<li><a href="/html/register.html">
|
||||
<div class="buttons">Register</div>
|
||||
</a></li>
|
||||
<li><a href="/html/login.html">
|
||||
<div class="buttons">Login</div>
|
||||
</a></li>
|
||||
<li><a href="/html/updateinfo.html">
|
||||
<div class="buttons">Update Info</div>
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
<a href="login.html">Login Page</a>
|
||||
|
||||
<a href="updateinfo.html">Update Info Page</a>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
// Checking if the stream is down and displays a message if it is.
|
||||
async function isStreamDown() {
|
||||
|
||||
const response = await fetch(`https://cdn.chaos.stream/hls/src/maya.m3u8`);
|
||||
|
||||
if (response.status === 404) {
|
||||
document.getElementById("streamframe").style.display = "none";
|
||||
document.getElementById("stream").style.border = 'double'
|
||||
document.getElementById("stream").innerHTML = 'Currently not streaming.'
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = isStreamDown()
|
||||
</script>
|
||||
|
||||
<main>
|
||||
|
||||
<section>
|
||||
|
||||
<div id="streamchat">
|
||||
<div id="stream">
|
||||
<iframe id="streamframe" src="https://live.on.chaos.stream/maya" title="mayas stream"
|
||||
scrolling="no"></iframe>
|
||||
</div>
|
||||
|
||||
<iframe id="chatbox" src="/html/chat.html" title="chat box" scrolling="no"></iframe>
|
||||
|
||||
</nav>
|
||||
<div id="streamchat">
|
||||
<div id="stream">
|
||||
<h1>Test Text</h1>
|
||||
</div>
|
||||
|
||||
<iframe id="chatbox" src="chat.html" title="chat box"></iframe>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</section>
|
||||
|
||||
</html>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,148 @@
|
|||
// VARIABLES
|
||||
let messageCount = 0;
|
||||
let username = localStorage.getItem('username');
|
||||
const form = document.querySelector('form');
|
||||
|
||||
// SEND A MESSAGE
|
||||
|
||||
// GRABS MESSAGE FROM FORM & SENDS
|
||||
form.addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(form);
|
||||
|
||||
formMessage = formData.get('message').toString();
|
||||
|
||||
//KINDA UNNECESSARY
|
||||
//CHECKS TO SEE IF THE PERSON IS LOGGED IN IN ORDER TO SEND A MESSAGE.
|
||||
const response = await fetch(`../api/token/${username}/`);
|
||||
const matches = await response.json();
|
||||
|
||||
//YES THIS IS CONFUSING I KNOW.
|
||||
if (matches.status === "ok") {
|
||||
sendMessage()
|
||||
} else {
|
||||
const mismatch = 'Username and token mismatch. Try logging in again.'
|
||||
printText(mismatch.bold())
|
||||
logout()
|
||||
localStorage.removeItem('username')
|
||||
form.reset()
|
||||
}
|
||||
return formMessage;
|
||||
})
|
||||
|
||||
//SEND MESSAGE FETCH FUNCTION
|
||||
|
||||
async function sendMessage() {
|
||||
sendMessageInfo = { "name": username, "body": formMessage }
|
||||
fetch('../api/message/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendMessageInfo),
|
||||
})
|
||||
form.reset()
|
||||
modCommand()
|
||||
}
|
||||
|
||||
|
||||
// RECIEVE MESSAGES
|
||||
|
||||
let messageUpdate = window.setInterval(fetchMessages, 1000);
|
||||
|
||||
async function fetchMessages() {
|
||||
const response = await fetch('/api/message/messages.json');
|
||||
const recievedMessages = await response.json();
|
||||
document.getElementById("chatbox").innerHTML = ""
|
||||
|
||||
for (const message of recievedMessages) {
|
||||
|
||||
let leftBracket = '('
|
||||
let rightBracket = ')'
|
||||
let space = ' '
|
||||
|
||||
if (message.pronouns === '' || message.pronouns === 'none' || message.pronouns === null) {
|
||||
leftBracket = ''
|
||||
rightBracket = ''
|
||||
space = ''
|
||||
}
|
||||
printText(message.user.bold().toString() + space + "<i style='color:#b8d1e7'>" + leftBracket.small() + message.pronouns.small().toString() + rightBracket.small() + "</i>" + " : " + "<span style='color:#e4e4ef'>" + message.body.toString() + "</span>");
|
||||
}
|
||||
|
||||
|
||||
if (recievedMessages.length != messageCount) {
|
||||
let scroll = document.getElementById("chatbox");
|
||||
scroll.scrollTop = scroll.scrollHeight;
|
||||
}
|
||||
|
||||
messageCount = recievedMessages.length;
|
||||
}
|
||||
|
||||
|
||||
// FUNCTION TO PRINT MESSAGES IN THE CHAT BOX
|
||||
|
||||
function printText(text) {
|
||||
let p = document.createElement("p");
|
||||
const div = document.getElementById("chatbox");
|
||||
div.appendChild(p)
|
||||
p.innerHTML = text
|
||||
}
|
||||
|
||||
|
||||
//LOGGED IN STUFF
|
||||
//TODO ADD CHECK TO SEE IF USERNAME AND TOKEN MATCHES
|
||||
function loggedIn() {
|
||||
if (username === '' || username === null) {
|
||||
document.querySelector("#loggeduser").innerHTML = 'You are not logged in'
|
||||
} else {
|
||||
document.querySelector("#loggeduser").innerHTML = `You are logged in as "${username}"`
|
||||
}
|
||||
}
|
||||
|
||||
loggedIn()
|
||||
|
||||
//MODERATION
|
||||
|
||||
async function modCommand() {
|
||||
let action = ''
|
||||
let target = ''
|
||||
|
||||
if (formMessage.startsWith('/ban')) {
|
||||
action = "Ban"
|
||||
target = formMessage.replace('/ban ', '')
|
||||
sendCommand()
|
||||
} else if (formMessage.startsWith('/kick')) {
|
||||
action = "Kick"
|
||||
target = formMessage.replace('/kick ', '')
|
||||
sendCommand()
|
||||
} else if (formMessage.startsWith('/promote')) {
|
||||
action = "Promote"
|
||||
target = formMessage.replace('/promote ', '')
|
||||
sendCommand()
|
||||
} else if (formMessage.startsWith('/demote')) {
|
||||
action = "Demote"
|
||||
target = formMessage.replace('/demote ', '')
|
||||
sendCommand()
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
async function sendCommand() {
|
||||
let sendCommand = { "name": username, "action": action, "target": target }
|
||||
const response = await fetch('/api/mod/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendCommand),
|
||||
});
|
||||
if (response.status === 'ok') {
|
||||
return;
|
||||
} else {
|
||||
printText('Error Issuing Command. Are you an Admin or Mod?')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
let uname = document.querySelector('#uname').value;
|
||||
let pin = document.querySelector('#pin').value;
|
||||
const form = document.querySelector('form');
|
||||
let username = localStorage.getItem('username');
|
||||
|
||||
// SUBMIT FORM FUNCTION. AND FETCH USERNAME AND PIN FROM API.
|
||||
|
||||
|
@ -14,24 +15,43 @@ form.addEventListener("submit", async function (event) {
|
|||
uname = formData.get('uname');
|
||||
pin = formData.get('pin');
|
||||
|
||||
const response = await fetch(`/api/users/${uname}/${pin}`);
|
||||
const loginInfo = await response.json();
|
||||
try {
|
||||
const loginInfo = await loginFetch();
|
||||
|
||||
if (loginInfo.status === "ok") {
|
||||
login()
|
||||
} else {
|
||||
incorrectLogin()
|
||||
if (loginInfo.status === 'ok') {
|
||||
login()
|
||||
} else {
|
||||
incorrectLogin()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
document.querySelector("#errormessage").innerHTML = 'An Error has Occurred. Try again later. ' + e.toString();
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// LOGIN FETCH
|
||||
|
||||
async function loginFetch() {
|
||||
let sendLoginInfo = { "name": uname, "pin": pin }
|
||||
const res = await fetch('/api/login/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendLoginInfo),
|
||||
});
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
|
||||
// FUNCTIONS FOR WHETHER THE LOGIN WAS A SUCCESS OR FAILURE
|
||||
|
||||
function login() {
|
||||
console.log('You have logged in!')
|
||||
document.querySelector("#username").innerHTML = `Logged in as ${uname}`
|
||||
document.querySelector("#errormessage").innerHTML = ''
|
||||
localStorage.setItem("username", `${uname}`);
|
||||
localStorage.setItem('username', `${uname}`);
|
||||
document.querySelector("#username").innerHTML = `Logged in as ${uname}`
|
||||
window.location.replace("/index.html")
|
||||
}
|
||||
|
||||
function incorrectLogin() {
|
|
@ -0,0 +1,32 @@
|
|||
// // //VARIBLES
|
||||
// myStorage = window.localStorage;
|
||||
// allCookies = document.cookie;
|
||||
|
||||
//LOGOUT FETCH FUNCTION
|
||||
|
||||
async function logout() {
|
||||
let sendLogoutInfo = { "name": username }
|
||||
fetch('/api/logout/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendLogoutInfo),
|
||||
});
|
||||
document.querySelector("#errormessage").innerHTML = 'Logged out.'
|
||||
document.getElementById("logoutbutton").style.display = "none";
|
||||
localStorage.removeItem('username')
|
||||
username = null;
|
||||
loggedIn()
|
||||
}
|
||||
|
||||
// // IF THERE IS A TOKEN BUT NO USERNAME LOGOUT
|
||||
// if (allCookies !== '' && myStorage.length === 0) {
|
||||
// logout()
|
||||
// }
|
||||
|
||||
// // IF THERE IS NO COOKIE BUT A USERNAME GET RID OF USERNAME LOCALLY.
|
||||
// if (allCookies === '' && myStorage.length !== 0) {
|
||||
// localStorage.removeItem('username')
|
||||
// document.querySelector("#loggeduser").innerHTML = 'You are not logged in'
|
||||
// }
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
//DECLARING VARIABLES AND GRABBING VALUES FROM FORM.
|
||||
|
||||
let uname = document.querySelector('#uname').value;
|
||||
let pin = document.querySelector('#pin').value;
|
||||
let selected = document.querySelector('#selected').value;
|
||||
let custom = document.querySelector('#custom').value;
|
||||
let pronouns = ''
|
||||
const form = document.querySelector('form');
|
||||
|
||||
//SUBMIT FUNCTION & CHECKING IF USERNAME IS TAKEN
|
||||
|
||||
form.addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(form);
|
||||
|
||||
uname = formData.get('uname');
|
||||
pin = formData.get('pin');
|
||||
selected = formData.get('selected');
|
||||
custom = formData.get('custom')
|
||||
|
||||
if (custom === '' && selected === 'none') {
|
||||
pronouns = ''
|
||||
} else if (custom !== '') {
|
||||
pronouns = custom
|
||||
} else {
|
||||
pronouns = selected
|
||||
}
|
||||
|
||||
|
||||
//IF THE API SUCCESSFULLY REGISTERS A USER THEN DO THIS
|
||||
try {
|
||||
const regRes = await isUnameTaken();
|
||||
|
||||
if (regRes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (regRes.status === 'ok') {
|
||||
document.querySelector("#errormessage").innerHTML = 'Registered!'
|
||||
window.location.replace("../html/login.html")
|
||||
} else {
|
||||
document.querySelector("#errormessage").innerHTML = 'Failed to register. Try again later.'
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
document.querySelector("#errormessage").innerHTML = 'An Error has Occurred. Try again later. ' + e.toString();
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//CHECKS IF A USERNAME IS TAKEN
|
||||
async function isUnameTaken() {
|
||||
|
||||
const response = await fetch(`/api/users/${uname}/`);
|
||||
const isTaken = await response.json();
|
||||
|
||||
//YES THIS IS CONFUSING I KNOW.
|
||||
if (isTaken.status === "fail") {
|
||||
return await register()
|
||||
} else {
|
||||
document.querySelector('#errormessage').innerHTML = `${uname} is already taken.`
|
||||
}
|
||||
}
|
||||
|
||||
//FETCH FUNCTIONS. GETTING USERNAME FROM API & REGISTERING USER ASSIGNED NAME AND PIN.
|
||||
|
||||
async function register() {
|
||||
let sendRegisterInfo = { "name": uname, "pin": pin, "pronouns": pronouns }
|
||||
const response = await fetch('../api/register/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendRegisterInfo),
|
||||
});
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
// function errorMessage() {
|
||||
// document.querySelector("#errormessage").innerHTML = 'An error has occurrred. Please try again later.'
|
||||
// }
|
|
@ -8,7 +8,6 @@ let newPin = document.querySelector('#newpin').value;
|
|||
const form = document.querySelector('form');
|
||||
let selected = document.querySelector('#selected').value;
|
||||
let custom = document.querySelector('#custom').value;
|
||||
let responseText;
|
||||
let updateEvent = ''
|
||||
let newEvent = ''
|
||||
let newPronouns = ''
|
||||
|
@ -27,6 +26,8 @@ form.addEventListener("submit", async function (event) {
|
|||
selected = formData.get('selected');
|
||||
custom = formData.get('custom')
|
||||
|
||||
|
||||
//SETS NEWPRONOUNS DEPENDING ON WHAT THE USER SELECTED/TYPED
|
||||
if (custom === '' && selected === 'none') {
|
||||
newPronouns = ''
|
||||
} else if (custom !== '') {
|
||||
|
@ -35,79 +36,116 @@ form.addEventListener("submit", async function (event) {
|
|||
newPronouns = selected
|
||||
}
|
||||
|
||||
//CHECKS IF THE USER IS CHANGING MORE THAN ONE TEXT FIELD AT A TIME
|
||||
let onlyChangeOne = document.querySelector("#errormessage").innerHTML = 'You can only change one at a time!'
|
||||
unameCheck()
|
||||
|
||||
//CHECKS IF THE USER IS CHANGING MORE THAN ONE TEXT FIELD AT A TIME
|
||||
if (newUname !== '' && newPin !== '') {
|
||||
onlyChangeOne
|
||||
document.querySelector("#errormessage").innerHTML = 'You can only change one at a time!'
|
||||
return;
|
||||
} else if (newUname !== '' && newPronouns !== '') {
|
||||
onlyChangeOne
|
||||
document.querySelector("#errormessage").innerHTML = 'You can only change one at a time!'
|
||||
return;
|
||||
} else if (newPin !== '' && newPronouns !== '') {
|
||||
onlyChangeOne
|
||||
document.querySelector("#errormessage").innerHTML = 'You can only change one at a time!'
|
||||
return;
|
||||
} else if (newUname !== '' && newPin !== '' && newPronouns !== '') {
|
||||
document.querySelector("#errormessage").innerHTML = 'You can only change one at a time!'
|
||||
return;
|
||||
} else {
|
||||
checkLoginInfo()
|
||||
}
|
||||
|
||||
|
||||
// ASSIGNS VARIABLES TO BE SENT TO API
|
||||
// ASSIGNS VARIABLES TO BE SENT TO API
|
||||
if (newUname === '' && newPin === '' && newPronouns !== '') {
|
||||
newEvent = newPronouns
|
||||
updateEvent = 'pronouns'
|
||||
updateEvent = 'Pronouns'
|
||||
} else if (newUname === '' && newPronouns === '' && newPin !== '') {
|
||||
newEvent = newPin
|
||||
updateEvent = 'pin'
|
||||
updateEvent = 'Pin'
|
||||
} else if (newPin === '' && newPronouns === '' && newUname !== '') {
|
||||
newEvent = newUname
|
||||
updateEvent = 'name'
|
||||
updateEvent = 'Name'
|
||||
} else if (newPin === '' && newUname === '' && newPronouns === '') {
|
||||
document.querySelector("#errormessage").innerHTML = 'Please enter a new name, pin, or pronouns!'
|
||||
return;
|
||||
} else {
|
||||
checkLoginInfo()
|
||||
}
|
||||
|
||||
//CHECKS IF USERNAME IS TAKEN
|
||||
const isTaken = await getUname();
|
||||
|
||||
if (isTaken.status === 'ok') {
|
||||
document.querySelector("#errormessage").innerHTML = `username ${newUname} is already taken! `
|
||||
//IF THE API SUCCESSFULLY UPDATES INFO FOR A USER THEN DO THIS
|
||||
try {
|
||||
const updateResponse = await loginStatus();
|
||||
|
||||
if (updateResponse == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateResponse.status === 'ok') {
|
||||
document.querySelector("#errormessage").innerHTML = 'Login Changed!'
|
||||
window.location.replace("../html/login.html")
|
||||
} else {
|
||||
document.querySelector("#errormessage").innerHTML = 'Failed to update info. Try again later.'
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
document.querySelector("#errormessage").innerHTML = 'An Error has Occurred. Try again later. ' + e.toString();
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//CHECKS IF A USERNAME IS TAKEN
|
||||
async function unameCheck(){
|
||||
if (newUname !== '') {
|
||||
const response = await fetch(`api/users/${newUname}/`);
|
||||
const isTaken = await response.json();
|
||||
|
||||
// FETCH FUNTIONS. FETCHING USERNAME TO SEE IF ITS TAKEN.
|
||||
|
||||
async function getUname() {
|
||||
let response = await fetch(`/api/users/${newUname}`);
|
||||
responseJson = await response.json();
|
||||
return responseJson;
|
||||
if (isTaken.status === "ok") {
|
||||
document.querySelector('#errormessage').innerHTML = `${newUname} is already taken.`
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CHECKS IF THE LOGIN IS A SUCCESS
|
||||
async function loginStatus() {
|
||||
const loginInfo = await checkLoginInfo();
|
||||
|
||||
//FETCH FUNCTION TO UPDATE USER INFO
|
||||
|
||||
//TODO ADD CHECKING THE TOKEN with LOGIN IN IF STATEMENT
|
||||
//CHECKING IF THE USER CAN LOGIN WITH GIVEN CURRENT USERNAME AND PIN
|
||||
async function checkLoginInfo() {
|
||||
const response = await fetch(`/api/users/${uname}/${pin}`);
|
||||
const loginInfo = await response.json();
|
||||
|
||||
if (loginInfo.status === "ok") {
|
||||
updateInfo()
|
||||
if (loginInfo.status === 'ok') {
|
||||
return await updateInfo()
|
||||
} else {
|
||||
incorrectLogin()
|
||||
}
|
||||
}
|
||||
|
||||
//TODO ADD CHECKING THE TOKEN WITH LOGIN IN IF STATEMENT
|
||||
//CHECKING IF THE USER CAN LOGIN WITH GIVEN CURRENT USERNAME AND PIN
|
||||
// LOGIN FETCH
|
||||
|
||||
async function checkLoginInfo() {
|
||||
let sendLoginInfo = { "name": uname, "pin": pin }
|
||||
const res = await fetch('/api/login/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendLoginInfo),
|
||||
});
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
//FETCH FUNCTION TO UPDATE USER INFO
|
||||
|
||||
async function updateInfo() {
|
||||
let sendUpdateInfo = { "name": uname, "pin": pin, "changed_event": updateEvent, "new_event": newEvent }
|
||||
fetch('/api/users/change', {
|
||||
const response = await fetch('/api/change', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendUpdateInfo),
|
||||
});
|
||||
//document.querySelector("#errormessage").innerHTML = 'Login Changed!'
|
||||
//window.location.replace("/login.html")
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
function incorrectLogin() {
|
|
@ -1,62 +0,0 @@
|
|||
|
||||
//DECLARING VARIABLES AND GRABBING VALUES FROM FORM.
|
||||
|
||||
let uname = document.querySelector('#uname').value;
|
||||
let pin = document.querySelector('#pin').value;
|
||||
let selected = document.querySelector('#selected').value;
|
||||
let custom = document.querySelector('#custom').value;
|
||||
let pronouns = ''
|
||||
let responseText;
|
||||
const form = document.querySelector('form');
|
||||
|
||||
//SUBMIT FUNCTION &CHECKING IF USERNAME IS TAKEN
|
||||
|
||||
form.addEventListener("submit", async function (event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(form);
|
||||
|
||||
uname = formData.get('uname');
|
||||
pin = formData.get('pin');
|
||||
selected = formData.get('selected');
|
||||
custom = formData.get('custom')
|
||||
|
||||
if (custom !== '') {
|
||||
pronouns = custom
|
||||
} else {
|
||||
pronouns = selected
|
||||
}
|
||||
|
||||
try {
|
||||
const isNotTaken = await getUname();
|
||||
|
||||
if (isNotTaken.status === "fail") {
|
||||
register()
|
||||
} else {
|
||||
document.querySelector("#errormessage").innerHTML = `${uname} is already taken.`
|
||||
}
|
||||
} catch {
|
||||
document.querySelector("#errormessage").innerHTML = 'An Error has Occurred. Try again later.'
|
||||
}
|
||||
})
|
||||
|
||||
//FETCH FUNCTIONS. GETTING USERNAME FROM API & REGISTERING USER ASSIGNED NAME AND PIN.
|
||||
|
||||
async function getUname() {
|
||||
let response = await fetch(`/api/users/${uname}`);
|
||||
responseJson = await response.json();
|
||||
return responseJson;
|
||||
}
|
||||
|
||||
async function register() {
|
||||
let sendRegisterInfo = { "name": uname, "pin": pin, "pronouns": pronouns }
|
||||
fetch('/api/register/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(sendRegisterInfo),
|
||||
});
|
||||
document.querySelector("#errormessage").innerHTML = 'Registered!'
|
||||
window.location.replace("/login.html")
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
html {
|
||||
background: #F7A8B8;
|
||||
text-align: center;
|
||||
font-family: "Lucida Console", "Courier New", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
margin-top: 3%;
|
||||
}
|
||||
|
||||
#streamchat {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
|
||||
}
|
||||
|
||||
#stream {
|
||||
background-color: white;
|
||||
padding: 5%;
|
||||
box-shadow: 10px 10px 10px black;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-right: 4em;
|
||||
}
|
||||
|
||||
#chatbox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-left: 4em;
|
||||
}
|
||||
|
138
src/auth.rs
138
src/auth.rs
|
@ -1,4 +1,5 @@
|
|||
extern crate log;
|
||||
use uuid::Uuid;
|
||||
use crate::file_io::*;
|
||||
use rocket::http::{Cookie, Cookies};
|
||||
use crate::user::*;
|
||||
|
@ -19,14 +20,18 @@ pub fn register(data: Json<RegisterEvent>) -> JsonValue {
|
|||
} else {
|
||||
let pin_hashed = sha1::Sha1::from(&data.pin).digest().to_string(); // hash the pin
|
||||
|
||||
let new_user: User = User {
|
||||
let mut new_user: User = User {
|
||||
name: data.name.to_string().to_lowercase(),
|
||||
pin_hashed,
|
||||
pronouns: data.pronouns.to_string().to_lowercase(),
|
||||
session_token: "NULL".to_string(),
|
||||
role: UserType::Normal,
|
||||
|
||||
id: Uuid::new_v4(),
|
||||
};
|
||||
|
||||
if new_user.name == "admin".to_string() { // if name is admin, make them an admin
|
||||
new_user.role = UserType::Admin;
|
||||
}
|
||||
db_add(&new_user);
|
||||
|
||||
info!(
|
||||
|
@ -272,6 +277,7 @@ pub fn get_user(name: String) -> JsonValue {
|
|||
"name": user.name.to_lowercase(),
|
||||
"pronouns": user.pronouns,
|
||||
"role": user.role,
|
||||
"id": user.id.to_string(),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
@ -281,6 +287,117 @@ pub fn get_user(name: String) -> JsonValue {
|
|||
});
|
||||
}
|
||||
}
|
||||
// Make a user into a moderator
|
||||
fn premote(name: &str) -> JsonValue {
|
||||
if let Some(mut user) = db_read_user(&name.to_lowercase()).ok().flatten() {
|
||||
if user.role != UserType::Admin { // make sure mods can't demote admins ;3
|
||||
user.role = UserType::Moderator;
|
||||
db_remove(&user);
|
||||
db_add(&user);
|
||||
info!("succesfully premoted user {}", &user.name);
|
||||
return json!({
|
||||
"status": "ok",
|
||||
"reason": "premoted user",
|
||||
});
|
||||
} else {
|
||||
warn!("user is an admin, cannot make moderator");
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user is admin",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
warn!("could not premote {}, user not found", &name);
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user not found",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Make a user into a normal user
|
||||
fn demote(name: &str) -> JsonValue {
|
||||
if let Some(mut user) = db_read_user(&name.to_lowercase()).ok().flatten() {
|
||||
if user.role != UserType::Admin { // make sure mods can't demote admins ;3
|
||||
user.role = UserType::Normal;
|
||||
db_remove(&user);
|
||||
db_add(&user);
|
||||
info!("succesfully demoted user {}", &user.name);
|
||||
return json!({
|
||||
"status": "ok",
|
||||
"reason": "demoted user",
|
||||
});
|
||||
} else {
|
||||
warn!("user is an admin, cannot demote");
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user is admin",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
warn!("could not demote {}, user not found", &name);
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user not found",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Kick a user (temporarilly log them out for a certain amount of time)
|
||||
fn kick(name: &str) -> JsonValue {
|
||||
if let Some(mut user) = db_read_user(&name.to_lowercase()).ok().flatten() {
|
||||
if user.role != UserType::Admin { // make sure mods can't kick admins
|
||||
user.session_token = "NULL".to_string();
|
||||
db_remove(&user);
|
||||
db_add(&user);
|
||||
info!("succesfully kicked user {}", &user.name);
|
||||
return json!({
|
||||
"status": "ok",
|
||||
"reason": "kicked user",
|
||||
});
|
||||
} else {
|
||||
warn!("user is an admin, cannot kick");
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user is admin",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
warn!("could not kick {}, user not found", &name);
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user not found",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ban a user (remove their account)
|
||||
fn ban(name: &str) -> JsonValue {
|
||||
if let Some(mut user) = db_read_user(&name.to_lowercase()).ok().flatten() {
|
||||
if user.role != UserType::Admin { // make sure mods can't kick admins
|
||||
db_remove(&user);
|
||||
info!("succesfully banned user {}", &user.name);
|
||||
return json!({
|
||||
"status": "ok",
|
||||
"reason": "banned user",
|
||||
});
|
||||
} else {
|
||||
warn!("user is an admin, cannot ban");
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user is admin",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
warn!("could not ban {}, user not found", &name);
|
||||
return json!({
|
||||
"status": "fail",
|
||||
"reason": "user not found",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* User Management */
|
||||
#[post("/mod", format = "json", data = "<data>")]
|
||||
|
@ -303,18 +420,15 @@ pub fn moderation_actions(data: Json<ModerationAction>, mut cookies: Cookies) ->
|
|||
"reason": "NULL token",
|
||||
});
|
||||
} else if user.session_token == token.value() { // if token matches
|
||||
if user.role == UserType::Normal {
|
||||
if user.role == UserType::Moderator || user.role == UserType::Admin {
|
||||
match data.action {
|
||||
ModActions::Kick => {
|
||||
info!("kicked user {}", data.target)
|
||||
},
|
||||
ModActions::Ban => info!("banned user {}", data.target),
|
||||
_ => info!("F"),
|
||||
ModActions::Kick => kick(&data.target),
|
||||
ModActions::Ban => ban(&data.target),
|
||||
ModActions::Demote => demote(&data.target),
|
||||
ModActions::Premote => premote(&data.target),
|
||||
_ => return json!({"status":"fail","reason":"bad command"}),
|
||||
};
|
||||
return json!({
|
||||
"status": "ok",
|
||||
"reason": "completed action",
|
||||
});
|
||||
return json!({"status":"fail","reason":"idk"});
|
||||
} else {
|
||||
warn!("user does not have sufficient permissions to perform that action!");
|
||||
return json!({
|
||||
|
|
|
@ -42,6 +42,7 @@ fn create_message(message: Json<MessageInput>, user: &User) -> JsonValue {
|
|||
id: Uuid::new_v4(),
|
||||
event_type,
|
||||
user: user.name.to_lowercase().to_owned(),
|
||||
pronouns: user.pronouns.to_lowercase().to_owned(),
|
||||
body: message.body.to_string(),
|
||||
created_at: Utc::now(),
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ pub struct Message {
|
|||
pub id: Uuid,
|
||||
pub event_type: MessageType,
|
||||
pub user: String,
|
||||
pub pronouns: String,
|
||||
pub body: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/* User Data */
|
||||
// enum of different user types
|
||||
|
@ -17,6 +18,7 @@ pub struct User {
|
|||
pub pronouns: String, // user's pronouns
|
||||
pub session_token: String, // generated session token
|
||||
pub role: UserType, // type/role of user
|
||||
pub id: Uuid,
|
||||
}
|
||||
|
||||
/* Moderation Data */
|
||||
|
|
Loading…
Reference in New Issue