diff --git a/.env b/.env
index 15b7d07..ba74042 100644
--- a/.env
+++ b/.env
@@ -2,5 +2,4 @@ POSTGRES_USER="postgres"
POSTGRES_PASSWORD="localpw"
POSTGRES_URI="localhost"
POSTGRES_PORT="5432"
-POSTGRES_DB_NAME="forc_pub"
-DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_URI}/${POSTGRES_DB_NAME}"
+POSTGRES_DB_NAME="forc_pub"
\ No newline at end of file
diff --git a/README.md b/README.md
index 9a6320d..1773e08 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,111 @@
[![docs](https://docs.rs/forc/badge.svg)](https://docs.rs/forc/)
[![discord](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe)
-Welcome to the GitHub repository for [forc.pub](https://forc.pub/), the official package registry for the Sway programming language.
\ No newline at end of file
+Welcome to the GitHub repository for [forc.pub](https://forc.pub/), the official package registry for the Sway programming language.
+
+## How it Works
+
+`forc.pub` has a simple multi-threaded Rocket backend server which stores and fetches package metadata in a Postgres database. Packages are stored in a dedicated IPFS network, and package metadata is indexed and stored in [forc.pub-index](https://github.com/FuelLabs/forc.pub-index).
+
+The frontend uses React and TypeScript.
+
+## Forc Documentation
+
+For user documentation, including installing release builds, see: .
+
+## Building from Source
+
+This section is for local development of `forc.pub`.
+
+### Dependencies and Tools
+
+`forc.pub` is built in Rust and TypeScript. To begin, install the Rust toolchain following instructions at . Then configure your Rust toolchain to use Rust `stable`:
+
+```sh
+rustup default stable
+```
+
+If not already done, add the Cargo bin directory to your `PATH` by adding the following line to `~/.profile` and restarting the shell session.
+
+```sh
+export PATH="${HOME}/.cargo/bin:${PATH}"
+```
+
+You will also need to install [Node.js](https://nodejs.org/en/learn/getting-started/how-to-install-nodejs).
+
+To run the Postgres database locally, you will need [Docker](https://docs.docker.com/engine/install/).
+
+To connect to the database, you will need the [Diesel CLI](https://diesel.rs/guides/getting-started).
+
+Diesel is the Rust ORM used to create and run database migrations. It requires a separate C library called `libpq` to be installed as well.
+
+```sh
+# Mac only
+brew install libpq
+
+# Ubuntu only
+apt-get install libpq5
+
+# Install diesel CLI
+cargo install diesel_cli --no-default-features --features postgres
+
+# On macOS-arm64, you may need additional rust flags:
+RUSTFLAGS='-L /opt/homebrew/opt/libpq/lib' cargo install diesel_cli --no-default-features --features postgres
+```
+
+It is also recommended to install a Postgres client like [DBeaver](https://dbeaver.io/) to connect and inspect the database while developing locally.
+
+### Building the `forc.pub` server
+
+Clone the repository and build the Sway toolchain:
+
+```sh
+git clone git@github.com:FuelLabs/forc.pub.git
+cd forc.pub
+cargo build
+```
+
+Confirm the server built successfully:
+
+```sh
+cargo run --bin forc.pub
+```
+
+### Running the `forc.pub` server
+
+Before starting the server, the local database must be up and running.
+
+```sh
+./scripts/start_local_db.sh
+```
+
+Now we can run the server with:
+
+```sh
+cargo run
+```
+
+Alternatively, the server can be run locally with Docker, as it is in the deployed environment.
+
+```sh
+./scripts/start_local_server.sh
+
+# Force the server image to be rebuilt
+./scripts/start_local_server.sh -f
+```
+
+### Running the Frontend
+
+The frontend requires npm and node to be installed.
+
+```sh
+cd app
+npm i
+npm start
+```
+
+This will open http://localhost:3000 in your browser. By default, it will use the local server endpoint, so the local server must be running.
+
+## Contributing
+
+We are not currently accepting contributions to `forc.pub` as the MVP is still being developed.
diff --git a/app/src/constants.ts b/app/src/constants.ts
index 9fc5846..df8601f 100644
--- a/app/src/constants.ts
+++ b/app/src/constants.ts
@@ -1 +1,2 @@
-export const SERVER_URI = process.env.REACT_APP_SERVER_URI ?? "http://localhost:8080"
\ No newline at end of file
+export const SERVER_URI = process.env.REACT_APP_SERVER_URI ?? "http://localhost:8080";
+export const REDIRECT_URI = process.env.REACT_APP_REDIRECT_URI ?? "http://localhost:3000";
\ No newline at end of file
diff --git a/app/src/features/toolbar/components/UserButton.tsx b/app/src/features/toolbar/components/UserButton.tsx
index 0bb7a4d..42b650f 100644
--- a/app/src/features/toolbar/components/UserButton.tsx
+++ b/app/src/features/toolbar/components/UserButton.tsx
@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom';
import { useGithubAuth } from '../hooks/useGithubAuth';
import { useLocalSession } from '../../../utils/localStorage';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
+import { REDIRECT_URI } from '../../../constants';
export const GITHUB_CLIENT_ID = 'Iv1.ebdf596c6c548759';
@@ -98,7 +99,7 @@ function UserButton() {
].join(',');
window.open(
- `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}`,
+ `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${REDIRECT_URI}`,
undefined,
windowDimensions
);
diff --git a/app/src/features/toolbar/hooks/useGithubAuth.ts b/app/src/features/toolbar/hooks/useGithubAuth.ts
index 50a7550..122532a 100644
--- a/app/src/features/toolbar/hooks/useGithubAuth.ts
+++ b/app/src/features/toolbar/hooks/useGithubAuth.ts
@@ -112,12 +112,13 @@ export function useGithubAuth(): [AuthenticatedUser | null, () => void] {
response.user && setGithubUser(response.user);
if (response.error) {
console.log('session error: ', response.error);
+ clearSessionId();
}
})
.catch((error) => {
console.log('Unexpected error: ', error);
});
- }, [githubUser, sessionId, setGithubUser]);
+ }, [githubUser, sessionId, setGithubUser, clearSessionId]);
return [githubUser, logout];
}
diff --git a/deployment/Dockerfile b/deployment/Dockerfile
index 4891a79..03c136d 100644
--- a/deployment/Dockerfile
+++ b/deployment/Dockerfile
@@ -32,7 +32,7 @@ RUN cargo build
FROM ubuntu:22.04 as run
RUN apt-get update -y \
- && apt-get install -y --no-install-recommends ca-certificates curl git \
+ && apt-get install -y --no-install-recommends ca-certificates curl git libpq5 \
# Clean up
&& apt-get autoremove -y \
&& apt-get clean -y \
diff --git a/scripts/start_local_db.sh b/scripts/start_local_db.sh
index 4188f4f..a9af97e 100755
--- a/scripts/start_local_db.sh
+++ b/scripts/start_local_db.sh
@@ -1,5 +1,10 @@
#!/bin/bash
+# Source environment variables
+source .env
+NETWORK_NAME="forc_pub_net"
+CONTAINER_NAME="forc_pub_db"
+
# Check if Docker is installed
if ! command -v docker &> /dev/null; then
echo "Docker is not installed. Please install Docker to run this script."
@@ -7,15 +12,25 @@ if ! command -v docker &> /dev/null; then
fi
# Check if the PostgreSQL container is already running
-if docker ps --format '{{.Names}}' | grep -q '^postgres$'; then
+if docker ps --format '{{.Names}}' | grep -q ^$CONTAINER_NAME$; then
echo "PostgreSQL container is already running."
exit 0
fi
-# Source environment variables
-source .env
+# Create docker network if it does not exist
+if [ -z $(docker network ls --filter name=^${NETWORK_NAME}$ --format="{{ .Name }}") ] ; then
+ echo "Creating docker network ${NETWORK_NAME}."
+ docker network create $NETWORK_NAME
+fi
# Start PostgreSQL container
-docker run --name $POSTGRES_USER -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD -e POSTGRES_DB=$POSTGRES_DB_NAME -d -p $POSTGRES_PORT:$POSTGRES_PORT postgres
+docker run \
+ --rm -d \
+ --name $CONTAINER_NAME \
+ --network $NETWORK_NAME \
+ -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
+ -e POSTGRES_DB=$POSTGRES_DB_NAME \
+ -p $POSTGRES_PORT:$POSTGRES_PORT \
+ postgres
echo "PostgreSQL container started successfully."
\ No newline at end of file
diff --git a/scripts/start_local_server.sh b/scripts/start_local_server.sh
new file mode 100755
index 0000000..4b2fc86
--- /dev/null
+++ b/scripts/start_local_server.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+# Start the PostgreSQL container
+./scripts/start_local_db.sh
+
+# Source environment variables
+source .env
+
+CONTAINER_NAME="forc_pub_dev"
+NETWORK_NAME="forc_pub_net"
+DB_CONTAINER_NAME="forc_pub_db"
+FORCE_REBUILD=false
+
+# Check for command line arguments
+while getopts "f" opt; do
+ case ${opt} in
+ f)
+ FORCE_REBUILD=true
+ ;;
+ \?)
+ echo "Usage: $0 [-f]"
+ exit 1
+ ;;
+ esac
+done
+
+# Check if Docker image exists
+if $FORCE_REBUILD || [[ "$(docker images -q $DOCKER_IMAGE 2> /dev/null)" == "" ]]; then
+ echo "Building Docker image $DOCKER_IMAGE..."
+
+ # Build Docker image
+ docker build -t $CONTAINER_NAME -f deployment/Dockerfile .
+
+ # Check if build was successful
+ if [ $? -eq 0 ]; then
+ echo "Docker image $DOCKER_IMAGE built successfully."
+ else
+ echo "Failed to build Docker image $DOCKER_IMAGE."
+ exit 1
+ fi
+else
+ echo "Docker image $DOCKER_IMAGE already exists. Use -f flag to force rebuild."
+fi
+
+# Remove the container if it exists
+if [[ "$(docker ps -aqf name=$CONTAINER_NAME)" ]]; then
+ # Stop the container if it's running
+ if [[ "$(docker ps -q -f name=$CONTAINER_NAME)" ]]; then
+ echo "Stopping container $CONTAINER_NAME..."
+ docker stop $CONTAINER_NAME
+ if [ $? -eq 0 ]; then
+ echo "Container $CONTAINER_NAME stopped successfully."
+ else
+ echo "Failed to stop container $CONTAINER_NAME."
+ exit 1
+ fi
+ fi
+fi
+
+# Start the Docker container on the same network as the PostgreSQL container
+docker run \
+ --rm -d \
+ --name $CONTAINER_NAME \
+ --network $NETWORK_NAME \
+ -p 8080:8080 \
+ -e POSTGRES_USER=$POSTGRES_USER \
+ -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
+ -e POSTGRES_URI=$DB_CONTAINER_NAME \
+ -e POSTGRES_DB_NAME=$POSTGRES_DB_NAME \
+ $CONTAINER_NAME
+
+echo "Server container started successfully."
\ No newline at end of file
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 327cf5d..3014798 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -27,10 +27,8 @@ impl Default for Database {
impl Database {
pub fn new() -> Self {
// Create a connection pool
- dotenv().ok();
- let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let pool = Pool::builder()
- .build(ConnectionManager::::new(database_url))
+ .build(ConnectionManager::::new(db_url()))
.expect("db connection pool");
// Run migrations
@@ -53,3 +51,12 @@ impl Database {
pub(crate) fn string_to_uuid(s: String) -> Result {
Uuid::parse_str(s.as_str()).map_err(|_| DatabaseError::InvalidUuid(s))
}
+
+fn db_url() -> String {
+ dotenv().ok();
+ let user = env::var("POSTGRES_USER").expect("POSTGRES_USER must be set");
+ let password = env::var("POSTGRES_PASSWORD").expect("POSTGRES_PASSWORD must be set");
+ let uri = env::var("POSTGRES_URI").expect("POSTGRES_URI must be set");
+ let db_name = env::var("POSTGRES_DB_NAME").expect("POSTGRES_DB_NAME must be set");
+ format!("postgres://{user}:{password}@{uri}/{db_name}")
+}