first commit
This commit is contained in:
commit
2440d4ef22
BIN
__pycache__/config.cpython-312.pyc
Normal file
BIN
__pycache__/config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/database.cpython-312.pyc
Normal file
BIN
__pycache__/database.cpython-312.pyc
Normal file
Binary file not shown.
21
config.py
Normal file
21
config.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/python
|
||||
from configparser import ConfigParser
|
||||
import json
|
||||
|
||||
|
||||
def config(filename='database.ini', section='postgresql'):
|
||||
# create a parser
|
||||
parser = ConfigParser()
|
||||
# read config file
|
||||
parser.read(filename)
|
||||
|
||||
# get section, default to postgresql
|
||||
db = {}
|
||||
if parser.has_section(section):
|
||||
params = parser.items(section)
|
||||
for param in params:
|
||||
db[param[0]] = param[1]
|
||||
else:
|
||||
raise Exception('Section {0} not found in the {1} file'.format(section, filename))
|
||||
|
||||
return db
|
||||
6
database.ini
Normal file
6
database.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[postgresql]
|
||||
host = 192.168.1.67
|
||||
database = postgres
|
||||
user = test
|
||||
password = test
|
||||
port = 5432
|
||||
106
database.py
Normal file
106
database.py
Normal file
@ -0,0 +1,106 @@
|
||||
import config, psycopg2
|
||||
|
||||
def tupleDictionaryFactory(columns, row):
|
||||
columns = [desc[0] for desc in columns]
|
||||
return dict(zip(columns, row))
|
||||
|
||||
def create_messages():
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
with open('sql/CREATE/messages.sql') as file:
|
||||
sql = file.read()
|
||||
|
||||
cur.execute(sql)
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return None
|
||||
|
||||
def create_channels():
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
with open('sql/CREATE/channels.sql') as file:
|
||||
sql = file.read()
|
||||
|
||||
cur.execute(sql)
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return None
|
||||
|
||||
def get_channel(id):
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
|
||||
sql = f"SELECT * FROM channels WHERE id=%s;"
|
||||
cur.execute(sql, (id,))
|
||||
rows = cur.fetchone()
|
||||
if rows:
|
||||
rows = tupleDictionaryFactory(cur.description, rows)
|
||||
return rows
|
||||
return {}
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return {}
|
||||
|
||||
|
||||
def insert_message(payload):
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
with open('sql/INSERT/messages.sql') as file:
|
||||
sql = file.read()
|
||||
cur.execute(sql, payload)
|
||||
rows = cur.fetchone()
|
||||
if rows:
|
||||
rows = tupleDictionaryFactory(cur.description, rows)
|
||||
return rows
|
||||
return {}
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return {}
|
||||
|
||||
def select_message(id):
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
with open('sql/SELECT/messages.sql') as file:
|
||||
sql = file.read()
|
||||
cur.execute(sql, (id, ))
|
||||
rows = cur.fetchone()
|
||||
if rows:
|
||||
rows = tupleDictionaryFactory(cur.description, rows)
|
||||
return rows
|
||||
return {}
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return {}
|
||||
|
||||
def select_messages(payload):
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
with open('sql/SELECT/messagesByChannel.sql') as file:
|
||||
sql = file.read()
|
||||
cur.execute(sql, payload)
|
||||
rows = cur.fetchall()
|
||||
if rows:
|
||||
rows = [tupleDictionaryFactory(cur.description, row) for row in rows]
|
||||
return rows
|
||||
return []
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return []
|
||||
5
sql/CREATE/channels.sql
Normal file
5
sql/CREATE/channels.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS channels (
|
||||
id SERIAL PRIMARY KEY,
|
||||
channel_name char(64) NOT NULL,
|
||||
channel_description TEXT
|
||||
);
|
||||
11
sql/CREATE/messages.sql
Normal file
11
sql/CREATE/messages.sql
Normal file
@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
timestamp TIMESTAMP,
|
||||
channel_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
message_content TEXT NOT NULL,
|
||||
CONSTRAINT fk_channel_id
|
||||
FOREIGN KEY(channel_id)
|
||||
REFERENCES channels(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
4
sql/INSERT/messages.sql
Normal file
4
sql/INSERT/messages.sql
Normal file
@ -0,0 +1,4 @@
|
||||
INSERT INTO messages
|
||||
(timestamp, channel_id, user_id, message_content)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
RETURNING *;
|
||||
7
sql/SELECT/messages.sql
Normal file
7
sql/SELECT/messages.sql
Normal file
@ -0,0 +1,7 @@
|
||||
SELECT messages.*,
|
||||
row_to_json(users.*) as user,
|
||||
row_to_json(channels.*) As channel
|
||||
FROM messages
|
||||
LEFT JOIN users ON messages.user_id = users.id
|
||||
LEFT JOIN channels ON messages.channel_id = channels.id
|
||||
WHERE messages.id=%s;
|
||||
14
sql/SELECT/messagesByChannel.sql
Normal file
14
sql/SELECT/messagesByChannel.sql
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
SELECT * FROM (
|
||||
SELECT messages.*,
|
||||
row_to_json(users.*) as user,
|
||||
row_to_json(channels.*) As channel
|
||||
FROM messages
|
||||
LEFT JOIN users ON messages.user_id = users.id
|
||||
LEFT JOIN channels ON messages.channel_id = channels.id
|
||||
WHERE messages.channel_id = %s
|
||||
ORDER BY messages.timestamp DESC
|
||||
LIMIT %s
|
||||
) AS subquery
|
||||
ORDER BY subquery.timestamp ASC;
|
||||
1
static/css/uikit.min.css
vendored
Normal file
1
static/css/uikit.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
static/images/placeholder.webp
Normal file
BIN
static/images/placeholder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
1
static/js/uikit-icons.min.js
vendored
Normal file
1
static/js/uikit-icons.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/js/uikit.min.js
vendored
Normal file
1
static/js/uikit.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
365
templates/index.html
Normal file
365
templates/index.html
Normal file
@ -0,0 +1,365 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||
<title id="title"></title>
|
||||
|
||||
|
||||
<!-- Material Icons -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
<!-- Material Symbols - Outlined Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||
<!-- Material Symbols - Rounded Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||
<!-- Material Symbols - Sharp Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/uikit-icons.min.js') }}"></script>
|
||||
</head>
|
||||
<style>
|
||||
:root {
|
||||
--body-background: #121212;
|
||||
--background: #1c1c1c;
|
||||
--primary-color: #f7f7f7;
|
||||
--accent-color: #ffb3b3;
|
||||
--secondary-text: #666666;
|
||||
--highlight: #ffd700;
|
||||
--font: 'Arial', sans-serif;;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--body-background);
|
||||
color: var(--primary-color);
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
.subtitle{
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.my-nav-bar {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 10px;
|
||||
background-color: var(--body-background);
|
||||
box-shadow: 0 2px 5px var(--background);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.my-nav-bar inline {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-button{
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
background-color: transparent;
|
||||
color: var(--primary-color);
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s, box-shadow 0.3s;
|
||||
cursor: pointer;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.nav-button span {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.nav-title{
|
||||
margin: 0px;
|
||||
font-size: x-large;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
background-color: var(--body-background);
|
||||
min-height: calc(100vh - 120px);
|
||||
margin-bottom: 60px;
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chat-container > * {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
|
||||
.floating-square {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
background-color: var(--body-background);
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flexbox;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.full-width-input {
|
||||
padding: 10px;
|
||||
width: calc(100% - 70px);
|
||||
height: auto; /* Allow dynamic height */
|
||||
min-height: 48px; /* Starting height */
|
||||
resize: vertical; /* Allow vertical resizing */
|
||||
overflow: hidden;
|
||||
vertical-align: bottom;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-bottom: 2px solid var(--secondary-text);
|
||||
border-radius: 0px;
|
||||
transition: border-color 0.3s;
|
||||
outline: none;
|
||||
background-color: var(--background);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.full-width-input:focus {
|
||||
border-bottom-color: var(--primary-color); /* Highlight upon focus */
|
||||
}
|
||||
|
||||
.full-width-button {
|
||||
width: 50px;
|
||||
margin: 0;
|
||||
vertical-align: bottom;
|
||||
padding: 10px;
|
||||
min-width: 50px;
|
||||
border-radius: 4px; /* Match textarea */
|
||||
border: none; /* Match textarea */
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--background);
|
||||
color: var(--primary-color);
|
||||
transition: background-color 0.3s, box-shadow 0.3s;
|
||||
cursor: pointer;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.full-width-button:hover {
|
||||
background-color: var(--background); /* Darker tone on hover */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); /* Enhanced hover elevation */
|
||||
}
|
||||
|
||||
.full-width-button:active {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.message-card {
|
||||
padding: 10px;
|
||||
margin: 0px;
|
||||
background-color: var(--body-background);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.username {
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
font-family: var(--font);
|
||||
|
||||
}
|
||||
.timestamp {
|
||||
font-size: 0.9em;
|
||||
color: #b0b0b0;
|
||||
font-family: var(--font);
|
||||
}
|
||||
.message-content {
|
||||
margin-top: 5px;
|
||||
margin-left:50px;
|
||||
margin-right: 5px;
|
||||
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<div class="my-nav-bar">
|
||||
<inline>
|
||||
<a href="/logout" class="nav-button"><span class="material-icons">menu</span>Menu</a>
|
||||
<p id="room-title" class="nav-title">Room Title</p>
|
||||
</inline>
|
||||
</div>
|
||||
<div class="chat-container" >
|
||||
<div id="chat" style="height: auto;">
|
||||
</div>
|
||||
</div>
|
||||
<div id="test" class="floating-square">
|
||||
<textarea id="messageInput" class="full-width-input" placeholder="Type your thoughts..."></textarea>
|
||||
<button id="sendButton" onclick="sendMessage()" class="full-width-button"><span class="material-symbols-outlined">
|
||||
send
|
||||
</span></button>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
var socket = io.connect('http://192.168.1.45:5812/');
|
||||
var current_room = 1;
|
||||
var current_username = 'unknown';
|
||||
var current_channel;
|
||||
var user;
|
||||
|
||||
fetch('/get_session')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
user = data['user'] // Revel in the data!
|
||||
current_username = user[1]
|
||||
})
|
||||
.catch(error => console.error('Error fetching session:', error));
|
||||
|
||||
socket.on('messageReceive', function(data) {
|
||||
console.log('Received:', data);
|
||||
let message = data.message;
|
||||
// instead of adding the message do I just reload the messages? what kind of infranstructure would that require.
|
||||
addMessage([data])
|
||||
});
|
||||
|
||||
socket.on('joined', function(data) {
|
||||
current_channel = data
|
||||
document.getElementById('room-title').innerHTML = data['channel_name']
|
||||
});
|
||||
|
||||
const textarea = document.getElementById('messageInput');
|
||||
textarea.addEventListener('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = this.scrollHeight + 'px';
|
||||
document.getElementById('test').style.height = this.scrollHeight + 'px'
|
||||
});
|
||||
textarea.addEventListener('blur', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = '40px';
|
||||
document.getElementById('test').style.height = '50px'
|
||||
this.blur()
|
||||
});
|
||||
|
||||
textarea.addEventListener('keydown', function(event) {
|
||||
if (event.key === 'Enter' && !event.shiftKey) { // Checks if Enter is pressed without shift
|
||||
event.preventDefault();
|
||||
sendMessage(); // Replace with your function
|
||||
}
|
||||
});
|
||||
|
||||
joinRoom(current_room)
|
||||
|
||||
function addMessage(data){
|
||||
|
||||
let message_card = document.createElement('div')
|
||||
message_card.setAttribute('class', 'message-card')
|
||||
|
||||
let user_info = document.createElement('div')
|
||||
user_info.setAttribute('class', 'user-info')
|
||||
|
||||
let image = document.createElement('img')
|
||||
image.setAttribute('class', 'avatar')
|
||||
image.setAttribute('src', "static/images/placeholder.webp")
|
||||
|
||||
let username = document.createElement('span')
|
||||
username.setAttribute('class', 'username')
|
||||
username.innerHTML = data[0]['user']['username']
|
||||
|
||||
let timestamp = document.createElement('span')
|
||||
timestamp.setAttribute('class', 'timestamp')
|
||||
timestamp.innerHTML = data[0]['timestamp']
|
||||
|
||||
user_info.append(image)
|
||||
user_info.append(username)
|
||||
user_info.append(timestamp)
|
||||
|
||||
message_card.append(user_info)
|
||||
|
||||
let message = document.createElement('div')
|
||||
message.setAttribute('class', 'message-content')
|
||||
|
||||
test = `${data[0]['message_content']}`
|
||||
for(let i = 1; i < data.length; i++){
|
||||
test = test + `<br>${data[i]['message_content']}`
|
||||
}
|
||||
|
||||
message.innerHTML = `${test}`
|
||||
|
||||
message_card.append(message)
|
||||
|
||||
document.getElementById('chat').append(message_card)
|
||||
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
|
||||
function sendMessage(){
|
||||
let message = document.getElementById('messageInput')
|
||||
|
||||
let currentTime = new Date();
|
||||
let timeString = currentTime.toLocaleTimeString();
|
||||
let dateString = currentTime.toLocaleDateString();
|
||||
let dateTimeString = `${dateString} ${timeString}`;
|
||||
|
||||
socket.emit('messageSend', {'message_content': message.value, 'channel_id': current_room, 'user_id': user[0], 'timestamp': currentTime})
|
||||
message.value = '';
|
||||
|
||||
}
|
||||
|
||||
async function joinRoom(channel_id) {
|
||||
socket.emit('join', {'channel_id': channel_id});
|
||||
current_room = channel_id
|
||||
|
||||
const url = new URL('/get_channel_messsages', window.location.origin);
|
||||
url.searchParams.append('channel_id', channel_id);
|
||||
const response = await fetch(url);
|
||||
data = await response.json()
|
||||
messages = data.messages;
|
||||
console.log(messages.length)
|
||||
console.log(messages)
|
||||
let combinedMessages = []
|
||||
let previous_message;
|
||||
let num_messages = 0
|
||||
|
||||
let grouped = [], tempGroup = [messages[0]];
|
||||
let startTime = new Date(messages[0].timestamp);
|
||||
for (let i = 1; i < messages.length; i++) {
|
||||
if ((new Date(messages[i].timestamp) - startTime <= 300000) &&
|
||||
(messages[i].user.id === messages[i - 1].user.id)) {
|
||||
tempGroup.push(messages[i]);
|
||||
} else {
|
||||
grouped.push(tempGroup);
|
||||
tempGroup = [messages[i]];
|
||||
startTime = new Date(messages[i].timestamp);
|
||||
}
|
||||
}
|
||||
grouped.push(tempGroup);
|
||||
console.log(grouped)
|
||||
|
||||
for (let i = 0; i< grouped.length; i++){
|
||||
addMessage(grouped[i])
|
||||
}
|
||||
}
|
||||
|
||||
// To leave a room
|
||||
function leaveRoom(username, room) {
|
||||
socket.emit('leave', { username, room });
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
64
templates/login.html
Normal file
64
templates/login.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
|
||||
<title id="title"></title>
|
||||
|
||||
|
||||
<!-- Material Icons -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
<!-- Material Symbols - Outlined Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||
<!-- Material Symbols - Rounded Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded" rel="stylesheet" />
|
||||
<!-- Material Symbols - Sharp Set -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}"/>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/uikit-icons.min.js') }}"></script>
|
||||
</head>
|
||||
<style>
|
||||
:root {
|
||||
--body-background: #121212;
|
||||
--background: #1c1c1c;
|
||||
--primary-color: #f7f7f7;
|
||||
--accent-color: #ffb3b3;
|
||||
--secondary-text: #666666;
|
||||
--highlight: #ffd700;
|
||||
--font: 'Arial', sans-serif;;
|
||||
}
|
||||
|
||||
body, html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: var(--body-background);
|
||||
color: var(--primary-color);
|
||||
font-family: var(--font);
|
||||
}
|
||||
|
||||
.login-container {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<form action="/login" method="post">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username">
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password">
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
<script></script>
|
||||
</html>
|
||||
136
webserver.py
Normal file
136
webserver.py
Normal file
@ -0,0 +1,136 @@
|
||||
from flask import Flask, render_template, session, request, redirect, url_for, jsonify
|
||||
from flask_socketio import SocketIO, join_room, leave_room
|
||||
import psycopg2
|
||||
import config, database
|
||||
from functools import wraps
|
||||
from dateutil import parser
|
||||
import datetime
|
||||
import pytz, json
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = '11gs22h2h1a4h6ah8e413a45'
|
||||
|
||||
socketio = SocketIO(app)
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_user():
|
||||
if 'user_id' in session.keys() and session['user_id'] is not None:
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
sql = f"SELECT * FROM users WHERE id=%s;"
|
||||
cur.execute(sql, (session['user_id'],))
|
||||
user = cur.fetchone()
|
||||
session['user'] = user
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
return dict(username="")
|
||||
|
||||
return dict(
|
||||
user = user
|
||||
)
|
||||
|
||||
return dict(user=[])
|
||||
|
||||
def login_required(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if 'user' not in session or session['user'] == None:
|
||||
return redirect('/login')
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
@login_required
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/get_session')
|
||||
def get_session():
|
||||
return {'user': session.get('user')}
|
||||
|
||||
@app.route('/get_channel_messsages', methods=["GET"])
|
||||
def get_channel_messages():
|
||||
|
||||
channel_id = request.args['channel_id']
|
||||
limit = 100
|
||||
page = 1
|
||||
|
||||
offset = (page - 1) * limit
|
||||
|
||||
messages = database.select_messages((channel_id, limit))
|
||||
return jsonify(messages=messages)
|
||||
|
||||
@app.route('/logout', methods=['GET'])
|
||||
def logout():
|
||||
if 'user' in session.keys():
|
||||
session['user'] = None
|
||||
return redirect('/login')
|
||||
|
||||
@app.route('/login', methods=["POST", "GET"])
|
||||
def login():
|
||||
session.clear()
|
||||
|
||||
if request.method == "POST":
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
|
||||
database_config = config.config()
|
||||
with psycopg2.connect(**database_config) as conn:
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
sql = f"SELECT * FROM users WHERE username=%s;"
|
||||
cur.execute(sql, (username,))
|
||||
user = cur.fetchone()
|
||||
print(user)
|
||||
except (Exception, psycopg2.DatabaseError) as error:
|
||||
print(error)
|
||||
conn.rollback()
|
||||
print(password, user[2])
|
||||
if user and user[2] == password:
|
||||
print(password, user[2])
|
||||
session['user'] = user
|
||||
return redirect('/')
|
||||
|
||||
if 'user' not in session.keys():
|
||||
session['user'] = None
|
||||
|
||||
return render_template('login.html')
|
||||
|
||||
@socketio.on('join')
|
||||
def on_join(data):
|
||||
print(data)
|
||||
channel = database.get_channel(data['channel_id'])
|
||||
room = channel['channel_name']
|
||||
join_room(room)
|
||||
socketio.emit('joined', channel, to=request.sid)
|
||||
|
||||
@socketio.on('leave')
|
||||
def on_leave(data):
|
||||
username = data['username']
|
||||
room = data['room']
|
||||
print(f"{username} joined the {room}!")
|
||||
leave_room(room)
|
||||
|
||||
@socketio.on('messageSend')
|
||||
def handle_event(data):
|
||||
print('Received:', data)
|
||||
channel = database.get_channel(data['channel_id'])
|
||||
|
||||
payload = (
|
||||
datetime.datetime.now(),
|
||||
data['channel_id'],
|
||||
data['user_id'],
|
||||
data['message_content']
|
||||
)
|
||||
row = database.insert_message(payload)
|
||||
message = database.select_message(row['id'])
|
||||
print(message)
|
||||
message['timestamp'] = str(message['timestamp'])
|
||||
socketio.emit('messageReceive', message, to=channel['channel_name'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app, host="0.0.0.0", port=5812, debug=True)
|
||||
Loading…
x
Reference in New Issue
Block a user