Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
messages.json
.env

# Local Netlify folder
.netlify
127 changes: 59 additions & 68 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,71 @@
const loginDiv = document.getElementById("login");
const chatDiv = document.getElementById("chat");
const usernameInput = document.getElementById("username");
import { auth, db } from './firebase-helpers.js';
import { onAuthStateChanged, signOut } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";
import { collection, query, orderBy, onSnapshot } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js";

const messageInput = document.getElementById("message");
const messagesDiv = document.getElementById("messages");
const logoutButton = document.getElementById("logout-button");
const userInfoDiv = document.getElementById("user-info");

let user = JSON.parse(localStorage.getItem("chatUser"));

if (user) {
showChat();
setupPusher();
}

function login() {
const username = usernameInput.value.trim();
if (!username) return;

const id = "user_" + Math.random().toString(36).substring(2, 10);
user = { id, username };
localStorage.setItem("chatUser", JSON.stringify(user));
showChat();
setupPusher();
}

function showChat() {
loginDiv.style.display = "none";
chatDiv.style.display = "block";
loadMessages();
}

function sendMessage() {
const content = messageInput.value.trim();
if (!content) return;
let currentUser = null;

fetch("/.netlify/functions/send-message", {
method: "POST",
body: JSON.stringify({ ...user, content }),
headers: { "Content-Type": "application/json" },
})
.then(() => {
messageInput.value = "";
loadMessages(); // reload pesan setelah kirim
})
.catch((e) => {
console.error("Failed to send message", e);
// Render messages to the UI
function renderMessages(messages) {
messagesDiv.innerHTML = ""; // Clear existing messages
messages.forEach(message => {
const p = document.createElement("p");
p.textContent = `${message.username}: ${message.content}`;
messagesDiv.appendChild(p);
});
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}

function addMessage(data) {
const p = document.createElement("p");
p.textContent = `${data.username}: ${data.content}`;
messagesDiv.appendChild(p);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
// Listen for real-time messages
function listenForMessages() {
const q = query(collection(db, "messages"), orderBy("timestamp", "asc"));
onSnapshot(q, (querySnapshot) => {
const messages = [];
querySnapshot.forEach((doc) => {
messages.push(doc.data());
});
renderMessages(messages);
});
}

function setupPusher() {
Pusher.logToConsole = true; // aktifkan log untuk debug
// Send a message
window.sendMessage = async function() {
const content = messageInput.value.trim();
if (!content || !currentUser) return;

const pusher = new Pusher("3925ec66482126a69fde", {
cluster: "mt1",
});
try {
await fetch("/.netlify/functions/send-message", {
method: "POST",
body: JSON.stringify({ username: currentUser.email, content: content }),
headers: { "Content-Type": "application/json" },
});
messageInput.value = "";
} catch (error) {
console.error("Failed to send message:", error);
}
};

const channel = pusher.subscribe("chat");
channel.bind("message", function (data) {
addMessage(data);
// reload pesan agar sinkron dengan backend
loadMessages();
});
}
// Check user session and initialize
onAuthStateChanged(auth, (user) => {
if (user) {
currentUser = user;
userInfoDiv.textContent = `Anda login sebagai: ${currentUser.email}`;
listenForMessages();
} else {
window.location.href = 'login.html';
}
});

async function loadMessages() {
try {
const res = await fetch("/.netlify/functions/get-messages");
const messages = await res.json();
messagesDiv.innerHTML = ""; // clear sebelum render ulang
messages.forEach(addMessage);
} catch (e) {
console.error("Failed to load messages", e);
}
}
// Logout
logoutButton.addEventListener('click', async () => {
try {
await signOut(auth);
window.location.href = 'login.html';
} catch (error) {
console.error("Error signing out:", error);
}
});
21 changes: 21 additions & 0 deletions firebase-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
import { getAuth } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";
import { getFirestore } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js";

// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyD9JacaD9_pINVdYrdKsqtXwykQnJwWaBg",
authDomain: "chat-app-ca53a.firebaseapp.com",
projectId: "chat-app-ca53a",
storageBucket: "chat-app-ca53a.firebasestorage.app",
messagingSenderId: "815329800335",
appId: "1:815329800335:web:2e55d0e3f9fe754b6b80fa",
measurementId: "G-3VJP3Y2G3P"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);

export { auth, db };
35 changes: 20 additions & 15 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Group Chat</title>
<link rel="stylesheet" href="style.css"/>
<script src="https://js.pusher.com/7.2/pusher.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="login">
<h2>Masukkan Username</h2>
<input type="text" id="username" placeholder="Username"/>
<button onclick="login()">Login</button>
</div>

<div id="chat" style="display:none;">
<h2>Group Chat</h2>
<div id="chat">
<div class="chat-header">
<h2>Group Chat</h2>
<button id="logout-button">Logout</button>
</div>
<div id="user-info"></div>
<div id="messages"></div>
<input type="text" id="message" placeholder="Ketik pesan..."/>
<button onclick="sendMessage()">Kirim</button>
<div class="chat-input-area">
<input type="text" id="message" placeholder="Ketik pesan..." />
<button onclick="sendMessage()">Kirim</button>
</div>
</div>

<script src="app.js"></script>
<script src="app.js" type="module"></script>
<footer class="copyright">
<p>Created by Edinst</p>
</footer>
</body>
</html>
49 changes: 49 additions & 0 deletions login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login - Group Chat</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="auth-container">
<div class="auth-header">
<h2>Login ke Group Chat</h2>
</div>
<div class="auth-body">
<input type="email" id="email" placeholder="Email" />
<input type="password" id="password" placeholder="Password" />
<button id="login-button">Login</button>
<p>Belum punya akun? <a href="register.html">Daftar di sini</a></p>
</div>
</div>

<script type="module">
import { auth } from './firebase-helpers.js';
import { signInWithEmailAndPassword } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";

const loginButton = document.getElementById('login-button');
const emailInput = document.getElementById('email');
const passwordInput = document.getElementById('password');

loginButton.addEventListener('click', async () => {
const email = emailInput.value;
const password = passwordInput.value;

try {
await signInWithEmailAndPassword(auth, email, password);
window.location.href = 'index.html';
} catch (error) {
alert('Gagal login: ' + error.message);
}
});
</script>
<footer class="copyright">
<p>Created by Edinst</p>
</footer>
</body>
</html>
5 changes: 0 additions & 5 deletions netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,3 @@
[dev]
functionsPort = 9999

[functions.environment]
PUSHER_APP_ID = "1977760"
PUSHER_KEY = "3925ec66482126a69fde"
PUSHER_SECRET = "918c29c07605a74e8d2b"
PUSHER_CLUSTER = "mt1"
21 changes: 0 additions & 21 deletions netlify/functions/get-messages.js

This file was deleted.

1 change: 0 additions & 1 deletion netlify/functions/messages.json

This file was deleted.

78 changes: 38 additions & 40 deletions netlify/functions/send-message.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,47 @@
require("dotenv").config(); // Load environment variables
const fs = require("fs-extra");
const path = require("path");
const Pusher = require("pusher");
const admin = require('firebase-admin');

const messagesPath = path.join(__dirname, "messages.json");
// Initialize Firebase Admin SDK only if it hasn't been initialized yet
if (!admin.apps.length) {
// Parse the service account JSON from the environment variable
const serviceAccount = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT_JSON);

const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_KEY,
secret: process.env.PUSHER_SECRET,
cluster: process.env.PUSHER_CLUSTER,
useTLS: true,
});
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
}

const db = admin.firestore();

exports.handler = async (event) => {
if (event.httpMethod !== "POST") {
return { statusCode: 405, body: "Method Not Allowed" };
if (event.httpMethod !== 'POST') {
return { statusCode: 405, body: 'Method Not Allowed' };
}

const data = JSON.parse(event.body);

// Read existing messages
let messages = [];
try {
messages = await fs.readJson(messagesPath);
} catch {
messages = [];
const { username, content } = JSON.parse(event.body);

if (!username || !content) {
return { statusCode: 400, body: 'Username and content are required.' };
}

const message = {
username,
content,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
};

const writeResult = await db.collection('messages').add(message);

return {
statusCode: 200,
body: JSON.stringify({ success: true, id: writeResult.id }),
};
} catch (error) {
// Log the error for debugging purposes
console.error('Error in send-message function:', error);
return {
statusCode: 500,
body: JSON.stringify({ error: error.message }),
};
}

// Add new message
const message = {
username: data.username,
content: data.content,
timestamp: Date.now(),
};
messages.push(message);

// Save messages
await fs.writeJson(messagesPath, messages);

// Trigger Pusher
await pusher.trigger("chat", "message", message);

return {
statusCode: 200,
body: JSON.stringify({ success: true }),
};
};
Empty file added netlify_dev.log
Empty file.
Loading