/avarts

Avarts (name change pending) is an open-source, self-hostable, and private fitness app with some of the basic features from Strava.

Primary LanguageTypeScriptMIT LicenseMIT

Avarts

Avarts (name change pending) is an open-source, self-hostable, and private fitness app with some of the basic features from Strava.



About The Project

Built With

Frontend

Backend

Getting Started

Docker is the quickest way of getting a local development server up and running.

Prerequisites

Installation

git clone https://github.com/ethanfann/avarts.git && cd avarts

Copy .env.example to .env

cp .env.example .env

Open .env and update the following:

  • MAPBOX_TOKEN
  • ADMIN_EMAIL
  • ADMIN_PASSWORD
  • ADMIN_FIRST_NAME
  • ADMIN_LAST_NAME

Admin .env variables will be used during database seed to create the default admin account.

Copy frontend/.env.example to frontend/.env. This contains an env variable, REACT_APP_SERVER_URL that points to the server.

cp frontend/.env.example frontend/.env

Build the Docker image

docker compose build

Run Docker compose

docker compose up

Start the frontend in a separate terminal window, which will open a webpage at localhost:3001 once done.

cd frontend && npm install && npm run start

Login with ADMIN_EMAIL and ADMIN_PASSWORD from .env

GraphQL

Download a GraphQL client like GraphiQL and point it to http://127.0.0.1:3000/graphql in order to execute queries and mutations against the server.

Types

All GraphQL responses are statically typed. Create a new file in app/graphql/types in order to add a new type.

Ex.

activity_comment_type.rb

module Types
  class ActivityCommentType < BaseModel
    field :comment, String, null: false
    field :user, UserType, null: false
  end
end

Queries

The general process for creating a new query is as follows:

  1. Create a resolver in app/graphql/resolvers/
  2. Append the resolver to app/graphql/types/query_type.rb

Ex.

app/graphql/resolvers/ping.rb

class Resolvers::Ping < GraphQL::Schema::Resolver
  type String, null: false
  description 'Ping Pong'

  def resolve
    'Pong'
  end
end
module Types
  class QueryType < BaseObject
    # ...
    field :ping, resolver: Resolvers::Ping
    # ...
  end
end=

Mutations

The general process for creating a new mutation is as follows:

  1. Create a resolver in app/graphql/mutations/
  2. Append the resolver to app/graphql/types/mutation_type

Ex.

app/graphql/mutations.add_activity_comment.rb

module Mutations
  class AddActivityComment < GraphQL::Schema::Mutation
    argument :comment, String, required: true
    argument :activity_id, ID, required: true
    argument :user_id, ID, required: true

    type Types::ActivityCommentType

    def resolve(comment: nil, activity_id: nil, user_id: nil)
      ActivityComment.create!(
        comment: comment,
        activity_id: activity_id,
        user_id: user_id,
      )
    end
  end
end

Codegen

GraphQL Code Generator is used to provide typed components and hooks for use in the frontend React SPA.

To generate a schema from Rails.

  1. Export the GraphQL Schema from Rails
rake graphql:schema:dump
  1. Run Codegen
cd frontend && npm run codegen

Note: This step is required after making any modifications to the Rails models or graphql types.

  1. Add the mutation/query in frontend/src/graphql/[mutations/query]

frontend/src/graphql/mutations/addActivityComment.mutation.ts

import gql from "graphql-tag";

export default gql`
  mutation AddActivityComment(
    $comment: String!
    $userId: ID!
    $activityId: ID!
  ) {
    addActivityComment(
      comment: $comment
      userId: $userId
      activityId: $activityId
    ) {
      id
    }
  }
`;
  1. Import a Query/Mutation

frontend/src/components/UploadForm.tsx

import { useAddActivityCommentMutation } from "../../generated/graphql";
const ActivityCommentBox = (props: Props) => {
  // ...
  const [addActivityCommentMutation] = useAddActivityCommentMutation()

  // ...

 const addComment = async (e: React.FormEvent, currentUser: UserType) => {
    e.preventDefault()
    if (currentUser && currentUser.id && activityId) {
      await addActivityCommentMutation({
        variables: {
          comment: comment,
          userId: currentUser.id,
          activityId: activityId,
        },
        refetchQueries: ['myActivites'],
      })
    }
    // ...
  }

   return (
     // ...
   )
}

Terraform

Terraform is used to manage the creation of AWS resources for running in production. Currently this includes:

  • S3: Stores user avatar and Mapbox static activity ride images.
  • Cloudfront: Serves S3 assets over a CDN.
  1. Install Terraform
  2. Verify an AWS profile has been created in ~/.aws.
  3. Run the Terraform commands below
terraform init
terraform plan
terraform apply

Contributing

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE for more information.

Contact

Ethan Fann - github@ethanfann.com

Project Link: https://github.com/ethanfann/avarts