TLRL (too long; read later)—add, search and archive bookmarks. I use it to manage my bookmarks and to learn backend development, specifically on the Spring+React stack.
- Project Overview
- Quick start
Here's a quick overview of the application architecture, project structure and code organization.
A high level view of the overall architecture.
│ TLRL │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Spring App │ │ PostgreSQL │ │
┌─────┴───┴───┐ │<─────────────>│ │ │
│ React │ │ └─────────────┘ │
│ Web App │─┐ │ ┌───────┐ │ ┌─────┐
└─────┬───────┘ | │ │ │ ┌────────────┐ │ │ ┌──┴──┐
│ │ ┌───V──┐ │ │ Kafka │ │ Fetcher <─┼───┼────│ │ www │
┌─────────────┐ │ │ │ │ │<──┼─> <─┼──>│ │ │ └──│ │
│ Bookmarklet │─┼───┼─┼─> │ │ │ │ └──────┬─────┘ │ └─────┘
└─────────────┘ │ │ │ API │ │ └───────┘ | │
│ │ │ │ │ ┌──────v─────┐ │
│ │ │ │ │<─────────────>│ Solr │ │
│ │ └──────┘ │ │ Search │ │
│ └─────────────┘ └────────────┘ │
│ │
The system consist of the following components:
- Spring Boot application that serves:
- API for clients for managing bookmarks
- React web client app (talks with our API)
- bookmarklet for saving bookmarks (talks with our API)
- PostgreSQL database for persistence
- Fetcher for:
- fetching bookmarked pages
- converting fetched pages to pdf
- extract fetched page text to Solr index
- Solr provides search
The project structure is a monorepo, with sub projects resembling the components in the diagram above. A brief description of each sub project and code organization below:
backendproject containing the application (API, security) code and database schema. Maven style layout, with packages organized mostly by feature, then layer:bookmarkcontains bookmark feature (e.g, self-contained controller, repository, service)securitysecurity related code used across the overall applicationcorecore domain/models used across the applicationsupporthelper/utilities used across the applicationuseruser feature (e.g, self-contained repository, service)
frontendproject code for single page web client (and bookmarklet) that talks with the backend API. Note, we build the frontend and deploy it with the main application code. Typicalcreate-react-applayout, organized mostly by layer:componentspagessupport
fetcherproject code for the Fetcher app, it runs standalone and organized as:index.jsmain application entry pointindexer.jswrapper for Solr JSON apibrowser.jswrapper for puppeteer
solrconfiguration files for Solr search engine
The quick start instructions are for running a demo of the application or getting started with development. In either case you'll need to fork/clone the project: https://github.com/ikumen/tlrl.git and the following requirements.
You'll need the following software:
- Java 8 at a minimum
- Docker with docker-compose
- configure at least one OAuth provider to handle authentication—GitHub may be easier to set up than Google as the latter requires creating a project if you don't already have one.
- sign into GitHub and navigate to: https://docs.github.com/en/developers/apps/creating-an-oauth-app
- fill in anything for "Application Name", "Homepage URL" and "Application description"
- fill in
http://localhost:9080/login/oauth2/code/githubfor "Authorization callback URL" - and "Register application"
- on the next screen, take note of the "Client ID" and "Client Secret", and continue below
- sign into Google Cloud Platform and navigate to: https://console.cloud.google.com/apis/credentials (you may need to create a project if you don't already have one)
- click the "+ CREATE CREDENTIALS" button at the top
- select "OAuth client ID"
- on the next screen, select "Web application" for Application type
- fill in anything for "Name"
- add
http://localhost:9080URI for "Authorized JavaScript origins" - add
http://localhost:9080/login/oauth2/code/googleURI for "Authorized redirect URIs" - and "Create", a "OAuth client created" dialog will pop up with your "Client ID" and "Client Secret", take note of it and continue below
Once you have the "Client ID" and "Client Secret", create a local properties file to store the secrets and make sure it doesn't get checked into source control.
Create the file at backend/src/main/resources/application-local.yml and add the following:
spring:
security:
oauth2:
client:
registration:
google:
client-id: <your-google-client-id>
client-secret: <your-google-client-secret>
github:
client-id: <your-github-client-id>
client-secret: <your-github-client-secret>Technically you can name application-local.yml anything, but I've already configured the following:
.gitignoreis already configured to ignoreapplication-local.ymlbackend/src/main/resources/application.ymlis already configured to pull in thelocalprofile
Just remember to update the gitignore with your custom "local" file name, so it doesn't accidentally get committed.
To run a demo of TLRL (assuming you've completed the requirements above), we use docker-compose
to bring up the application and its dependent services.
docker-compose up zookeeper kafka solr nginx tlrl-app tlrl-fetcherThe build for all the dependent services, fetcher and the backend application should take several minutes. If all goes well, point your browser to http://localhost:9080 to check out the application. Note, http://localhost:9080 is actually the proxy to the underlying app at http://localhost:8080.
For development, you'll need to install Nodejs/npm in addition to the requirements above. I usually start the dependent services ...
docker-compose up zookeeper kafka solr tlrl-fetcherthen run the backend application manually as I'm developing.
# Install the frontend dependencies and build it (which copies it over to backend application for deployment)
npm install --prefix frontend && npm run build --prefix frontend# Run the backend application
SPRING_PROFILES_ACTIVE=dev,h2 ./mvnw -f backend/pom.xml spring-boot:run -Ddb=h2Point your browser to http://localhost:8080.
To run PostgreSQL in development vs the embedded H2, just run the included Docker image.
docker-compose up zookeeper kafka solr postgres tlrl-fetcherThen run the Flyway migrations to create the schema.
FLYWAY_USER=postgres FLYWAY_PASSWORD=postgres ./mvnw -f backend/pom.xml flyway:migrate -Ddb=postgresNext, run the backend application manually as before.
npm install --prefix frontend && npm run build --prefix frontend
SPRING_PROFILES_ACTIVE=dev,postgres TLRL_DB_USER=tlrladmin TLRL_DB_USER_PASSWORD=tlrladmin TLRL_DB_SERVER_HOSTNAME=localhost TLRL_DB_SERVER_PORT=5432 TLRL_DB_NAME=tlrldb ./mvnw -f backend/pom.xml spring-boot:run -Ddb=postgres

