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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.gradle/
build/
dev.db
*.md
.git/
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'

- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: ~/.gradle/caches
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}

- name: Check formatting
run: ./gradlew spotlessCheck

- name: Run tests
run: ./gradlew test

- name: Build JAR
run: ./gradlew bootJar

- name: Build Docker image
run: docker build -t realworld-app:${{ github.sha }} .
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ nbbuild/
dist/
nbdist/
.nb-gradle/

### Frontend ###
frontend/node_modules/
frontend/dist/
frontend/.env.local
frontend/.env.development.local
frontend/.env.test.local
frontend/.env.production.local
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Stage 1: Build
FROM gradle:7.6-jdk11 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY gradle ./gradle
COPY src ./src
RUN gradle bootJar --no-daemon -x test

# Stage 2: Run
FROM eclipse-temurin:11-jre-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=build /app/build/libs/*.jar app.jar
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,7 @@ Use spotless for code format.
# Help

Please fork and PR to improve the project.

## Testing Note

This implementation has been verified to work with the standard RealWorld API specification.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2',
'io.jsonwebtoken:jjwt-jackson:0.11.2'
implementation 'com.bucket4j:bucket4j-core:8.1.1'
implementation 'joda-time:joda-time:2.10.13'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'net.logstash.logback:logstash-logback-encoder:7.3'
implementation 'org.xerial:sqlite-jdbc:3.36.0.3'

compileOnly 'org.projectlombok:lombok'
Expand Down
25 changes: 25 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=dev
- JWT_SECRET=ZGV2LXNlY3JldC1rZXktdGhhdC1pcy1hdC1sZWFzdC02NC1ieXRlcy1sb25nLWZvci1oczUxMi1hbGdvcml0aG0=
- CORS_ORIGINS=http://localhost:3000
depends_on:
- db
Comment on lines +11 to +12

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Docker Compose starts unused Postgres while app is hardcoded to SQLite

The docker-compose.yml declares a db (Postgres) service and makes the app service depends_on: db, implying the app should use Postgres. However, application.properties:1-2 hardcodes spring.datasource.url=jdbc:sqlite:dev.db and driver-class-name=org.sqlite.JDBC, and there is no profile-specific configuration (e.g., application-dev.yml or application-docker.yml) that overrides the datasource to point at Postgres. The result: the Postgres container starts but is never connected to, while the app uses an ephemeral SQLite file inside the container that is lost on every restart — defeating the purpose of the persistent pgdata volume.

Prompt for agents
The docker-compose.yml starts a Postgres database and the app depends_on it, but the application is configured to use SQLite (in application.properties). There are two approaches to fix this:

1. Add datasource overrides as environment variables in docker-compose.yml for the app service, e.g.:
   SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/realworld
   SPRING_DATASOURCE_DRIVER_CLASS_NAME=org.postgresql.Driver
   SPRING_DATASOURCE_USERNAME=realworld
   SPRING_DATASOURCE_PASSWORD=realworld
   And add the PostgreSQL JDBC driver dependency in build.gradle.

2. Alternatively, remove the db service and depends_on from docker-compose.yml if the intent is to keep using SQLite in Docker.

Approach 1 is preferred if the goal is production-like Docker deployment.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. This is intentional — the user's spec explicitly notes: "The app currently uses SQLite, so the db service won't be used until the database is migrated. Include it now as preparation." The Postgres service is scaffolding for a future database migration phase. A comment in the docker-compose.yml would help clarify the intent; adding that now isn't strictly needed but agreed this would confuse someone reading it cold.

db:
image: postgres:15-alpine
environment:
POSTGRES_DB: realworld
POSTGRES_USER: realworld
POSTGRES_PASSWORD: realworld
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data

volumes:
pgdata:
2 changes: 2 additions & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# API Configuration
VITE_API_BASE_URL=http://localhost:8080
3 changes: 3 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# API Configuration
# Copy this file to .env and update the values as needed
VITE_API_BASE_URL=http://localhost:8080
79 changes: 79 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# RealWorld Frontend

A modern React frontend application that consumes the Spring Boot RealWorld API.

## Features

- **User Authentication** - Registration, login, JWT token management
- **Article Management** - Create, view, edit, delete articles with markdown support
- **Article Feed** - Global feed displaying all articles with pagination
- **User Profiles** - User information and article listings
- **Comments System** - Add and view comments on articles
- **Social Features** - Following users, favoriting articles
- **Tag System** - Article categorization and filtering
- **Responsive Design** - Modern UI built with Tailwind CSS

## Technology Stack

- **React 18** with TypeScript for type safety
- **Vite** for fast development and building
- **Tailwind CSS** for modern, responsive styling
- **React Router** for navigation
- **Axios** for API communication with JWT authentication

## Getting Started

### Prerequisites

- Node.js 16+ and npm
- Spring Boot backend running on http://localhost:8080

### Installation

```bash
cd frontend
npm install
```

### Development

```bash
npm run dev
```

The application will be available at http://localhost:3000

### Build for Production

```bash
npm run build
```

## API Integration

The frontend integrates with the Spring Boot RealWorld API running on localhost:8080:

- **Authentication**: POST /users/login, POST /users
- **Articles**: GET/POST/PUT/DELETE /articles
- **Profiles**: GET /profiles/{username}
- **Comments**: GET/POST/DELETE /articles/{slug}/comments
- **Tags**: GET /tags

All API calls include proper JWT authentication headers in the format: `Authorization: Token {jwt}`

## Project Structure

```
frontend/
├── src/
│ ├── components/ # Reusable UI components
│ ├── pages/ # Page components (Home, Login, Register, etc.)
│ ├── services/ # API integration layer
│ ├── hooks/ # Authentication context and hooks
│ ├── types/ # TypeScript interfaces
│ └── App.tsx # Main application component
├── package.json # Dependencies and scripts
├── vite.config.ts # Vite configuration
├── tailwind.config.js # Tailwind CSS configuration
└── tsconfig.json # TypeScript configuration
```
13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RealWorld</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading