Code Monkey home page Code Monkey logo

golinks's Introduction

golinks

Mainpage

404 with Link suggestions

This application is deployed at https://go.d1m.dev. Signup is enabled for view only mode.

This is an implementation of Go Links powered by Next.js, GraphQL through PostGraphile and Auth0.

In short, Go Links are a type of URL Shorteners. You can create an alias that points to an URL and will redirect the user to that URL.

Please check the Related section to have a glance on how other companies and universities leverage go links.

Related

Other implementations

Feature Checklist

These are just a few ideas that come in my mind. Please feel free to suggest more features by creating an Issue. I'd love to hear your thoughts.

Contributions for the following are very welcome.

  • Create Links
  • Delete Links
  • Edit Links
  • Redirect Links
  • Auth
    • Can be disabled
    • Powered by Auth0
  • Security
    • Row Level Security using Auth0 Roles and Permissions
  • Link Description
  • Link Suggestion on 404
  • Link Usage Metrics
    • Number: Usage Total Count
    • Graph: Usage of last 31 days
  • Link Ownership
  • Link Parameters
    • For example, a gh alias with url https://github.com/$1/$2 allows https://go/gh/armand1m/golinks to be possible.
  • Link Groups (Folders)
    • URL: Accept / and can be redirected
    • UI: Folds URL groups
  • Private Links
  • Temporary Links
  • Random Alias
  • Help section
  • Chrome Plugin

Usage

Aliases created can be accessed through your deployment URL + the alias name. (e.g.: https://go.d1m.dev/twitter redirects to my twitter)

Chrome Custom Search Engine

This allows Chrome to recognize the "go" keyword in the address bar. Type "go", a space and then the alias for your link.

  • Go to chrome://settings/searchEngines > Other search engines > Add
  • Search engine: golinks
  • Keyword: go
  • URL with %s in place of query: https://go.mydomain.com/%s

Deploying

A Docker Image is available at Docker Hub: https://hub.docker.com/r/armand1m/golinks

Deploying to Kubernetes (GKE + Cloud SQL)

NOTE: This application used to be deployed on GKE with a self-hosted postgres instance. Over time, I've had many issues with both cost of this setup, and the degraded performance. As of 2023, this application is now hosted on https://fly.io, which offered a much simpler and cheaper option to run this application while also being faster and easier to maintain. That said, I kept the kubernetes manifests for future reference, or in case I end up coming back to kubernetes for side projects for any reason :)

Make sure to change the manifests accordingly to your environment.

Check the ./kubernetes folder for k8s manifests content. These manifests deploy the application together with a cloud_sql_proxy sidecar to allow networking with Google Cloud SQL.

Create a secret to keep the connection string:

kubectl create secret generic golinks-database \
  --from-literal=connectionstring='postgres://<user>:<pass>@<host>:5432/golinks'

Create a secret to keep the Cloud SQL service account:

kubectl create secret generic cloudsql-service-account \
  --from-file=service-account.json=./service-account.json

Create a secret to keep Auth0 ids and secrets:

kubectl create secret generic auth0-properties \
  --from-literal=client_id='auth0-app-client-id' \
  --from-literal=client_secret='auth0-app-client-secret' \
  --from-literal=cookie_secret='random-cookie-secret'

Export needed environment variables for envsubst:

export GOOGLE_CLOUD_PROJECT=<gcp-project>
export GOOGLE_CLOUD_REGION=<gcp-region>
export CLOUDSQL_INSTANCE_NAME=<cloud-sql-instance-name>
export HOSTNAME=go.mydomain.com
export PROTO=https
export LOGONAME=golinks
export AUTH0_ENABLED=true
export AUTH0_DOMAIN=<auth0-domain>
export AUTH0_AUDIENCE=<auth0-audience>
export AUTH0_COOKIE_DOMAIN=go.mydomain.com
export AUTH0_REDIRECT_URL=https://go.mydomain.com/api/callback
export AUTH0_POST_LOGOUT_REDIRECT_URL=https://go.mydomain.com

Create a deployment and service:

cat ./kubernetes/deployment.yaml | envsubst | kubectl apply -f -
kubectl apply -f ./kubernetes/service.yaml

Istio

Make sure to change the manifests accordingly to your environment.

Create the virtual service and destination rules:

# switch for the name of your gateway
export ISTIO_GATEWAY_NAME=istio-ingressgateway
export HOSTNAME=go.mydomain.com

cat ./kubernetes/istio/virtual-service.yaml | envsubst | kubectl apply -f -
kubectl apply -f ./kubernetes/istio/destination-rule.yaml

Authentication

This app leverages Auth0 as an Identity provider. Auth0 is used to manage users and their permissions to access and modify data in this application.

Enable Auth0

To enable, make sure you set the AUTH0_ENABLED env var as true.

In case this is set to false, every other environment variable prefixed with AUTH0_ can be considered optional.

Configuring Auth0

In the future, these steps will be automated through the Auth0 Provider for Terraform.

Create a Regular Web Application:

It's important that it is a Regular Web Application since this is a Next.js app. It also relies on the accessToken being a JWT token, so the server can extract roles and permissions from Auth0.

Setup callback and logout urls:

Setup the callback and logout url's to redirect to your domain + the route.

E.g.:

Callback URL: http://localhost:3000/api/callback Post Logout Redirect URL: http://localhost:3000

Keep the audience, domain, client_id and client_secret for easy access, as you'll need these to spin up the server (both in development and production)

Create the following roles and permissions:

I'm using YAML here to give a better representation of how the permissions should be setup in Auth0 roles:

role: editor
permissions:
- create:golinks
- update:golinks
- delete:golinks
role: viewer
permissions:
- read:golinks

These roles are used by Postgraphile when setting up a transaction for a query in a specific request context. This allows us to leverage Row Level Security through Postgres Policies to avoid access to data in the source of truth.

These roles are also used in the frontend to avoid rendering features for the user.

Create an user and assign roles:

Create an user and assign both the editor and viewer roles so you have access to all features.

Developing

armand1m/golinks is a Next.js app using GraphQL.

The database must be a Postgres 12.x database as the GraphQL API is generated using Postgraphile and leverages features like Row Level Security only available from Postgres 9.6+.

PostGraphile is then used as a NPM module and served through Next.js routes itself, so you don't have to worry about CORS, and the API is initialized together with the Next.js application.

GraphQL Type definitions are generated on application startup during development, so make sure your database executed the initialization scripts during startup as PostGraphile will infer them to the generate the type-defs.graphqls file. (This brings some caveats when making breaking changes in the database schema during development time, but easy to overcome.)

graphql-let then is used to generate type definitions in Typescript for development use.

Local Database without Auth0 in Watch mode:

For development, we use the official postgres docker image. Migrations need to be ran manually using dbmate and the SQL scripts provided.

Start the database:

docker-compose up -d db

Run the migrations using dbmate:

export DATABASE_URL=postgres://dev:[email protected]:5432/golinks?sslmode=disable
dbmate up

Regenerate the ./lib/type-defs.graphqls with:

npx postgraphile \
  --connection 'postgres://dev:[email protected]:5432/golinks' \
  --schema public \
  --export-schema-graphql ./lib/type-defs.graphqls \
  --subscriptions \
  --dynamic-json \
  --no-setof-functions-contain-nulls \
  --no-ignore-rbac \
  --no-ignore-indexes \
  --show-error-stack=json \
  --extended-errors hint,detail,errcode \
  --append-plugins @graphile-contrib/pg-simplify-inflector \
  --enable-query-batching \
  --legacy-relations omit \
  --no-server

Create an .env.local file (with auth0 disabled):

cat > ./.env.local <<EOL
DATABASE_CONNECTION_STRING=postgres://dev:[email protected]:5432/golinks
DATABASE_SCHEMA=public
NODE_ENV=development
AUTH0_ENABLED=false
PROTO=http
HOSTNAME=localhost:3000
LOGONAME=go.localhost
EOL

Download dependencies and run in development mode:

yarn
yarn dev

Access http://localhost:3000 and you should have a live development environment running.

Locally, with docker, local db and Auth0:

cat > ./.env.local <<EOL
DATABASE_CONNECTION_STRING=postgres://dev:dev@db:5432/golinks
DATABASE_SCHEMA=public
NODE_ENV=production
AUTH0_ENABLED=true
AUTH0_DOMAIN=<auth0-domain>
AUTH0_AUDIENCE=<auth0-audience>
AUTH0_CLIENT_ID=<auth0-client-id>
AUTH0_CLIENT_SECRET=<auth0-client-secret>
AUTH0_COOKIE_SECRET=<auth0-cookie-secret>
AUTH0_COOKIE_DOMAIN=localhost
AUTH0_REDIRECT_URL=http://localhost:3000/api/callback
AUTH0_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
HOSTNAME=localhost:3000
PROTO=http
LOGONAME=go.mydomain.dev
EOL

docker-compose up

Access http://localhost:3000

Locally, with docker, cloud sql db and Auth0:

# Environment Variables for the Application
cat > ./.env.cloud <<EOL
DATABASE_CONNECTION_STRING=postgres://<postgraphile-user>:<postgraphile-user-password>@db:5432/golinks
DATABASE_SCHEMA=public
NODE_ENV=production
AUTH0_ENABLED=true
AUTH0_DOMAIN=<auth0-domain>
AUTH0_AUDIENCE=<auth0-audience>
AUTH0_CLIENT_ID=<auth0-client-id>
AUTH0_CLIENT_SECRET=<auth0-client-secret>
AUTH0_COOKIE_SECRET=<auth0-cookie-secret>
AUTH0_COOKIE_DOMAIN=localhost
AUTH0_REDIRECT_URL=http://localhost:3000/api/callback
AUTH0_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
HOSTNAME=localhost:3000
PROTO=http
LOGONAME=go.mydomain.dev
EOL

# Environment Variables for the Cloud SQL Proxy
export GCP_KEY_PATH="~/cloud-sql-service-account.json"
export CLOUDSQL_INSTANCE="<gcp-project>:<gcp-region>:<cloud-sql-instance-name>=tcp:0.0.0.0:5432"

docker-compose -f ./docker-compose-cloud-sql.yml up

Cleaning Local Database

./clean-local-database.sh

Building docker image

docker build . -t armand1m/golinks

License

MIT © Armando Magalhaes

golinks's People

Contributors

armand1m avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

golinks's Issues

fix: move link usage metrics creation to the backend

Describe the bug
Currently, link usage metrics are reported from the frontend while redirecting the user. This makes it necessary to give INSERT permissions into the link_usage_metrics to anonymous users, which is a security risk and also unnecessary.

Moving it to the backend would make it safer and faster for the user (because we can reply to the request immediately and send metrics in the background).

feat: add auth0 terraform files

Is your feature request related to a problem? Please describe.
Yes, I am frustrated that to deploy this application and take advantage of Auth0 as an authentication provider, I have to go through an extensive manual configuration step to get Auth0 configured properly:

  • creating an application
  • setup login, logout, redirect url's
  • setup social identity providers
  • setup roles and permissions
  • setup rules

Describe the solution you'd like
I'd like to have a set of terraform files that I can use to bootstrap and maintain Auth0 state using infrastructure-as-code.

Describe alternatives you've considered
Integrating manually with Auth0 API is possible, but seems a lot of work when comparing to using a tool that does a decent job at managing infrastructure such as Terraform.

feat: a better database migration and seeding process

Is your feature request related to a problem? Please describe.
The current database migration and seeding are fairly trivial:

  • in development it leverages the default behaviour of the postgres docker image, to run all scripts provided in a folder when initializing an empty database.
  • in production, there is none (which is the problem)

Implementing a better database migration process would help to apply changes to the database schema in production safely.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.