| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
parent directory.. | ||||
A robust RESTful API built with Spring Boot 3.5, featuring JWT authentication, real-time WebSocket communication, and comprehensive content management capabilities.
Posts
Comments
WebSocket Communication
Notification System
backend/ ├── src/ │ ├── main/ │ │ ├── java/com/blog/backend/ │ │ │ ├── config/ # Application Configuration │ │ │ │ ├── security/ # Security & authentication │ │ │ │ │ ├── SecurityConfig.java │ │ │ │ │ ├── filters/ # 3-layer authentication filters │ │ │ │ │ ├── cors/ │ │ │ │ │ └── service/ │ │ │ │ ├── redis/ # Redis cache configuration │ │ │ │ ├── tomcat/ # Tomcat & rate limiting │ │ │ │ └── seed/ │ │ │ │ │ │ │ ├── controllers/ # REST API endpoints (12 controllers) │ │ │ ├── models/ # JPA entities (13 entities) │ │ │ │ └── base/ │ │ │ ├── services/ # Business logic layer │ │ │ │ ├── user/ │ │ │ │ ├── post/ │ │ │ │ ├── social/ │ │ │ │ └── media/ │ │ │ ├── repositories/ # Data access layer (12 repositories) │ │ │ ├── dto/ # Data Transfer Objects │ │ │ │ ├── user/ │ │ │ │ ├── post/ │ │ │ │ ├── comment/ │ │ │ │ ├── chat/ │ │ │ │ ├── report/ │ │ │ │ └── oauth2/ │ │ │ ├── websocket/ # WebSocket handlers │ │ │ ├── enums/ # Enumerations │ │ │ ├── exceptions/ # Exception handling │ │ │ ├── records/ # Java records │ │ │ └── Main.java │ │ │ │ │ └── resources/ │ │ ├── application.properties │ │ ├── db/migration/ # 14 Flyway migrations │ │ └── media/ # User-uploaded files │ │ │ └── test/ # Test sources │ ├── pom.xml ├── Dockerfile ├── docker-compose.yml ├── application-secret.properties └── README.md
cd backendCreate a PostgreSQL database:
CREATE DATABASE mydb;
CREATE USER amine WITH PASSWORD '1234';
GRANT ALL PRIVILEGES ON DATABASE mydb TO amine;spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=amine
spring.datasource.password=1234mvn clean install# Using Maven
mvn spring-boot:run
# Or using the run script
./run.sh
# Or run the JAR
java -jar target/backend-0.0.1-SNAPSHOT.jarThe API will be available at http://localhost:8080
Flyway migrations run automatically on startup. Migration files are in:
src/main/resources/db/migration/
Current migrations:
POST /api/users/register
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"password": "password123",
"description": "Bio text"
}POST /api/users/login
Authorization: Basic base64(email:password)
Response: JWT token stringGoogle OAuth2
GET /auth/google
# Redirects to Google login page
# Callback: GET /auth/google/callback?code={code}
# Redirects to: http://localhost:4200/login?token={jwt}GitHub OAuth2
GET /auth/github
# Redirects to GitHub authorization page
# Callback: GET /auth/github/callback?code={code}
# Redirects to: http://localhost:4200/login?token={jwt}Facebook OAuth2
GET /auth/facebook
# Redirects to Facebook login dialog
# Callback: GET /auth/facebook/callback?code={code}
# Redirects to: http://localhost:4200/login?token={jwt}OAuth2 Flow:
POST /api/users/me
Authorization: Bearer <jwt_token>GET /api/users/profile/{id}
Authorization: Bearer <jwt_token>PATCH /api/users/update
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"avatar": "filename.png",
"description": "Updated bio"
}DELETE /api/users/delete
Authorization: Bearer <jwt_token>POST /api/posts/create
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"title": "Post Title",
"description": "Post content...",
"banner": "image.png",
"categories": [1, 2, 3]
}GET /api/posts/{id}
Authorization: Bearer <jwt_token>GET /api/posts/?id=1&ownerId=2&searsh=keyword&category=Technology&savedPosts=true&offset=0
Authorization: Bearer <jwt_token>PATCH /api/posts/update
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"id": 1,
"title": "Updated Title",
"description": "Updated content",
"banner": "new-image.png",
"categories": [1, 2]
}DELETE /api/posts/delete/{post_id}
Authorization: Bearer <jwt_token>POST /api/posts/interactions/toggle/{post_id}
Authorization: Bearer <jwt_token>POST /api/posts/saves/toggle/{post_id}
Authorization: Bearer <jwt_token>POST /api/comments/create/{parentId}
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"content": "Comment text",
"postId": 1
}
# For nested replies, parentId is the parent comment ID
# For top-level comments, parentId is the post IDGET /api/comments/{postId}
Authorization: Bearer <jwt_token>DELETE /api/comments/delete/{id}
Authorization: Bearer <jwt_token>POST /api/comments/interactions/toggle/{comment_id}
Authorization: Bearer <jwt_token>POST /api/users/follow/toggle/{id}
Authorization: Bearer <jwt_token>GET /api/users/followers/{id}
Authorization: Bearer <jwt_token>GET /api/users/followings/{id}
Authorization: Bearer <jwt_token>GET /api/users/friends
Authorization: Bearer <jwt_token>POST /api/notifications
Authorization: Bearer <jwt_token>POST /api/notifications/{id}
Authorization: Bearer <jwt_token>POST /api/notifications/
Authorization: Bearer <jwt_token>POST /api/reports/
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"reportedId": 1,
"issue": "Spam",
"description": "This is spam content",
"priority": "HIGH",
"postId": 1, # Optional
"commentId": null, # Optional
"userId": null # Optional
}POST /api/reports/all
Authorization: Bearer <jwt_token>DELETE /api/reports/{report_id}
Authorization: Bearer <jwt_token>PATCH /api/reports/toggle/{report_id}
Authorization: Bearer <jwt_token>POST /api/admin/numbers
Authorization: Bearer <jwt_token>
Response:
{
"userCount": 100,
"postCount": 500,
"commentCount": 1500,
"reportCount": 10
}POST /api/admin/users
Authorization: Bearer <jwt_token>POST /api/admin/posts
Authorization: Bearer <jwt_token>DELETE /api/admin/users/delete/{id}
Authorization: Bearer <jwt_token>POST /api/admin/posts/hide/{id}
Authorization: Bearer <jwt_token>DELETE /api/admin/posts/delete/{id}
Authorization: Bearer <jwt_token>PATCH /api/users/ban/{id}
Authorization: Bearer <jwt_token>POST /api/chat/get/{id}
Authorization: Bearer <jwt_token>GET /api/media/{filename}The security system uses 3 ordered filters:
Token Details:
Token Generation:
String token = Jwts.builder()
.setSubject(String.valueOf(userId))
.claim("role", userRole)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 12 * 60 * 60 * 1000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();Allowed Origin: http://localhost:4200 (Angular frontend)
Allowed Methods:
Credentials: Allowed (for session cookies)
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role VARCHAR(50) DEFAULT 'ROLE_USER',
is_banned BOOLEAN DEFAULT FALSE,
is_online BOOLEAN DEFAULT FALSE,
avatar TEXT, -- Changed to TEXT for long OAuth2 URLs
description TEXT,
provider VARCHAR(50), -- OAuth2 provider (google, github, facebook)
provider_id VARCHAR(255), -- User ID from OAuth2 provider
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
description TEXT NOT NULL,
banner VARCHAR(255),
is_hidden BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE
);CREATE TABLE comments (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
post_id BIGINT REFERENCES posts(id) ON DELETE CASCADE,
parent_id BIGINT REFERENCES comments(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE interactions (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
post_id BIGINT REFERENCES posts(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, post_id)
);Endpoint: /ws
Protocol: STOMP over SockJS
Authentication: JWT token in connection query parameter
ws://localhost:8080/ws?token=<jwt_token>
/user/{userId}/queue/notifications - Receive notifications
/user/{userId}/queue/chat - Receive messages
/topic/user-status - User online/offline status
/app/chat - Send chat message
@MessageMapping("/chat")
public void sendMessage(@Payload MessageDTO message, Principal principal) {
// Process and send message
messagingTemplate.convertAndSendToUser(
String.valueOf(receiverId),
"/queue/chat",
messageDTO
);
}public void sendNotification(long recipientId, FullNotification notification) {
messagingTemplate.convertAndSendToUser(
String.valueOf(recipientId),
"/queue/notifications",
notification
);
}public enum NotifType {
LIKE, // Post or comment liked
COMMENT, // New comment on post
FOLLOW, // New follower
NEW_POST // New post from followed user
}# Application Name
spring.application.name=backend
# Database Configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=amine
spring.datasource.password=1234
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=false
# Flyway Configuration
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
# Session Configuration
server.servlet.session.timeout=1m
server.servlet.session.cookie.max-age=1m
server.servlet.session.tracking-modes=cookie
server.servlet.session.cookie.same-site=lax
server.servlet.session.cookie.secure=false
# File Upload Configuration
spring.servlet.multipart.max-request-size=10MB
# Media Configuration
app.media.base-url=${MEDIA_BASE_URL:http://localhost:8080/api/media/}
# Redis Configuration
spring.redis.host=localhost
spring.redis.port=6379
# OAuth2 Secrets (imported from application-secret.properties)
spring.config.import=file:application-secret.propertiesSet in SecurityConfig or environment:
private static final String SECRET = "this-is-a-very-strong-secret-key-12345678";
private static final long EXPIRATION_TIME = 12 * 60 * 60 * 1000; // 12 hoursRedis Cache Setup:
Configure Redis connection in application.properties:
spring.redis.host=localhost
spring.redis.port=6379Cache Configuration (RedisConfig.java):
Starting Redis:
# Using Docker
docker run -d -p 6379:6379 redis:latest
# Or install locally
# macOS: brew install redis
# Ubuntu: sudo apt-get install redis-serverConfigure OAuth2 providers in application.properties or environment variables:
Google OAuth2
google.client.id=your-google-client-id
google.client.secret=your-google-client-secretGitHub OAuth2
github.client.id=your-github-client-id
github.client.secret=your-github-client-secretFacebook OAuth2
facebook.client.id=your-facebook-app-id
facebook.client.secret=your-facebook-app-secretOAuth2 Provider Setup:
Google Cloud Console
GitHub Developer Settings
Facebook Developers
User Database Fields for OAuth2:
Path: ./src/main/resources/media/
Naming: UUID-based (e.g., 87a8d7c9-3914-45d3-b98f-846cff64b323.png)
Upload Limit: 10MB maximum file size
Base URL: Configurable via app.media.base-url (default: http://localhost:8080/api/media/)
Supported Formats:
The backend uses a comprehensive testing stack with JUnit 5 for unit/integration tests and REST Assured 5.3.1 for API endpoint testing.
JUnit 5: Modern testing framework for Java
REST Assured 5.3.1: Fluent API for testing REST services
Spring Boot Test: Testing utilities
# Run all tests
mvn test
# Run specific test class
mvn test -Dtest=UserServiceTest
# Run tests with coverage report
mvn test jacoco:report
# Run only unit tests
mvn test -Dgroups=unit
# Run only integration tests
mvn test -Dgroups=integration
# Skip tests during build
mvn clean package -DskipTests@Test
void testGetUserProfile() {
given()
.header("Authorization", "Bearer " + jwtToken)
.pathParam("id", userId)
.when()
.get("/api/users/profile/{id}")
.then()
.statusCode(200)
.body("firstName", equalTo("John"))
.body("email", equalTo("john@example.com"));
}To generate test coverage reports:
mvn clean test jacoco:report
# View report at: target/site/jacoco/index.htmlThe easiest way to run the entire backend stack including PostgreSQL:
# Navigate to backend directory
cd backend
# Start all services
docker-compose up
# Start in detached mode
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose downWhat it does:
docker-compose.yml:
version: '3.9'
services:
db:
image: postgres:18
environment:
POSTGRES_DB: mydb
POSTGRES_USER: amine
POSTGRES_PASSWORD: 1234
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U amine -d mydb"]
interval: 5s
retries: 5
backend:
build: .
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydb
SPRING_DATASOURCE_USERNAME: amine
SPRING_DATASOURCE_PASSWORD: 1234Dockerfile (Multi-stage build):
# Build stage
FROM maven:3.9.3-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
COPY mvnw .
COPY .mvn .mvn
COPY src src
RUN ./mvnw clean package -DskipTests
# Run stage
FROM eclipse-temurin:17-jdk
WORKDIR /app
COPY --from=build /app/target/backend-0.0.1-SNAPSHOT.jar app.jar
COPY application-secret.properties application-secret.properties
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]# Build the application
mvn clean package -DskipTests
# Run in production
java -jar target/backend-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod# Build the image
docker build -t blog-backend .
# Run PostgreSQL
docker run -d \
--name postgres \
-e POSTGRES_DB=mydb \
-e POSTGRES_USER=amine \
-e POSTGRES_PASSWORD=1234 \
-p 5432:5432 \
postgres:18
# Run backend (after DB is ready)
docker run -d \
--name blog-backend \
-p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/mydb \
-e SPRING_DATASOURCE_USERNAME=amine \
-e SPRING_DATASOURCE_PASSWORD=1234 \
blog-backendFor production deployments, override these via environment variables:
# Database
SPRING_DATASOURCE_URL=jdbc:postgresql://production-host:5432/mydb
SPRING_DATASOURCE_USERNAME=your_user
SPRING_DATASOURCE_PASSWORD=your_secure_password
# JWT
JWT_SECRET=your-very-strong-production-secret-key-minimum-44-bytes
# OAuth2 (if using)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
FACEBOOK_CLIENT_ID=your-facebook-app-id
FACEBOOK_CLIENT_SECRET=your-facebook-app-secretAll exceptions are handled centrally:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(GlobalException.class)
public ResponseEntity<String> handleGlobalException(GlobalException ex) {
return ResponseEntity.status(ex.getStatus()).body(ex.getMessage());
}
}For issues and questions, please contact the development team.
| Back | FazBrowse Home | New Git URL |