UnivTalk is a web application designed for university students to connect and collaborate.
Run the following command to install the necessary Go modules in the backend directory:
go mod tidyRun the application using:
go run main.goSetup .env file based on the provided .envExample file.
Before running the application, please setup your PostgreSQL database with the following schema:
CREATE TYPE user_status AS ENUM ('active', 'inactive');
CREATE TABLE IF NOT EXISTS categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT
);
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
uid UUID NOT NULL UNIQUE,
username VARCHAR(32) NOT NULL UNIQUE,
first_name VARCHAR(64) NOT NULL,
last_name VARCHAR(64) NOT NULL,
university VARCHAR(128) NOT NULL,
email VARCHAR(128) NOT NULL UNIQUE,
password VARCHAR(128) NOT NULL,
first_password VARCHAR(128) NOT NULL,
status user_status NOT NULL DEFAULT 'active',
salt VARCHAR(64) NOT NULL,
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS forums (
id SERIAL PRIMARY KEY,
fid UUID NOT NULL UNIQUE,
title VARCHAR(100) NOT NULL UNIQUE,
description TEXT,
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS forum_members (
user_id UUID NOT NULL REFERENCES users(uid) ON DELETE CASCADE,
forum_id UUID NOT NULL REFERENCES forums(fid) ON DELETE CASCADE,
role VARCHAR(10) NOT NULL CHECK (role IN ('admin', 'member')),
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, forum_id)
);
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
forum_id UUID NOT NULL REFERENCES forums(fid) ON DELETE CASCADE,
user_id UUID REFERENCES users(uid) ON DELETE SET NULL,
title VARCHAR(200) NOT NULL,
body TEXT NOT NULL,
media_url VARCHAR(255),
media_type VARCHAR(50),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS comments (
id SERIAL PRIMARY KEY,
post_id INTEGER NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(uid) ON DELETE SET NULL,
body TEXT NOT NULL,
parent_comment_id INTEGER REFERENCES comments(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS votes (
id SERIAL PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(uid) ON DELETE CASCADE,
post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
comment_id INTEGER REFERENCES comments(id) ON DELETE CASCADE,
value SMALLINT NOT NULL CHECK (value IN (1, -1)),
CONSTRAINT one_vote_per_user_on_target UNIQUE (user_id, post_id, comment_id),
CHECK (
(post_id IS NOT NULL AND comment_id IS NULL) OR
(post_id IS NULL AND comment_id IS NOT NULL)
)
);
INSERT INTO categories (name, description)
VALUES
('General Discussion', 'Ruang diskusi umum untuk topik apa saja seputar kehidupan universitas (mirip r/AskReddit).'),
('Computer Science & IT', 'Diskusi mengenai pemrograman, jaringan, AI, dan teknologi (mirip r/programming).'),
('Engineering', 'Komunitas untuk mahasiswa teknik sipil, mesin, elektro, dan arsitektur.'),
('Economics & Business', 'Diskusi seputar manajemen, akuntansi, saham, dan bisnis.'),
('Arts & Humanities', 'Ruang untuk seni, sastra, psikologi, dan ilmu sosial.'),
('Campus Life & Events', 'Informasi event kampus, UKM, rekomendasi kantin, atau info kosan.'),
('Career & Internships', 'Info lowongan magang, tips CV, dan persiapan karir setelah lulus.'),
('Thesis & Research', 'Tempat berkeluh kesah dan berbagi tips seputar Skripsi/Tesis (mirip r/GradSchool).'),
('Admissions & New Students', 'Panduan dan tanya jawab untuk mahasiswa baru (Maba).')
ON CONFLICT (name) DO NOTHING;Run the following command to install the necessary Node.js packages in the frontend directory:
npm install or
bun installRun the application using:
npm run devor
bun run devSetup .env file based on the provided .envExample file.
Base URL: http://localhost:8080 (Development)
Most endpoints are Protected Routes.
- Public Routes: Login, Register, Verify Token, Get Universities.
- Protected Routes: Require
Authorizationheader.
Header Format:
Authorization: Bearer <access_token_here>
- Endpoint:
POST /signup - Auth: Public
- Note:
statusmust be strictly"active"or"inactive"(database enum). - Body (JSON):
{ "username": "budi_santoso", "first_name": "Budi", "last_name": "Santoso", "university": "Universitas Indonesia", "email": "budi@ui.ac.id", "password": "passwordStrong123", "status": "active" }
- Endpoint:
POST /signin - Auth: Public
- Body (JSON): Can use
emailORusername.{ "email": "budi@ui.ac.id", "password": "passwordStrong123" } - Response Success:
{ "message": "Login Successful", "accessToken": "eyJh... (save this token)" }
- Endpoint:
GET /profile - Auth: Bearer Token
- Description: Get currently logged-in user data.
- Endpoint:
POST /verifytoken - Auth: Public
- Body (JSON):
{ "access_token": "eyJh..." }
- Endpoint:
GET /universities - Auth: Public
- Query Param:
?name=search_query(Optional)
- Endpoint:
GET /categories - Auth: Bearer Token
- Response: List of forum categories. Note that
idis an Integer.
- Endpoint:
GET /forums/ - Auth: Bearer Token
- Endpoint:
POST /forums/ - Auth: Bearer Token
- Body (JSON):
{ "title": "Golang Enthusiasts", "description": "Discuss everything about Go", "category_id": "1", } category_id: String containing the Integer ID of the category.
- Endpoint:
GET /forums/:forum_id - Param:
:forum_idis the UUID of the forum. - Auth: Bearer Token
- Endpoint:
GET /forums/:forum_id/members - Param:
:forum_idis the UUID of the forum. - Auth: Bearer Token
- Endpoint:
POST /forums/:forum_id/join - Auth: Bearer Token
- Endpoint:
POST /forums/:forum_id/leave - Auth: Bearer Token
- Endpoint:
GET /forums/:forum_id/posts - Auth: Bearer Token
- Endpoint:
POST /posts/ - Auth: Bearer Token
- Body (JSON):
{ "forum_id": "uuid-forum-id", "title": "How to handle cors in Gin?", "body": "I am having trouble with..." }
- Endpoint:
GET /posts/:post_id - Param:
:post_idis an Integer (from database Serial ID). - Auth: Bearer Token
- Endpoint:
DELETE /posts/:post_id - Auth: Bearer Token
- Endpoint:
GET /posts/:post_id/comments - Auth: Bearer Token
- Endpoint:
POST /comments/ - Auth: Bearer Token
- Body (JSON):
{ "post_id": 123, "body": "This is my comment.", "parent_comment_id": 0 } post_id: Integer ID of the post.parent_comment_id:0for top-level comments, orInteger IDof another comment to reply.
- Endpoint:
DELETE /comments/:comment_id - Auth: Bearer Token
- UpVote Endpoint:
POST /posts/:post_id/upvoteorPOST /comments/:comment_id/upvote - DownVote Endpoint:
POST /posts/:post_id/downvoteorPOST /comments/:comment_id/downvote - Remove Vote Endpoint:
POST /posts/:post_id/removevoteorPOST /comments/:comment_id/removevote - Auth: Bearer Token
- Param:
:post_idis an Integer;:comment_idis an Integer. - Description: Upvote, Downvote, or Remove Vote on a Post or Comment.