- Notifications
You must be signed in to change notification settings - Fork149
URL shortening service written in Go and React
License
short-d/short
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Install it fromChrome Web Store or build itfromsource
git clone https://github.com/short-d/short.git
- Go v1.13.1
- Node.js v12.12.0
- Yarn v1.19.1
- PostgreSQL v12.0
Copy
backend/.env.dist
file tobackend/.env
:cp backend/.env.dist backend/.env
Copy
frontend/.env.development.dist
file tofrontend/.env.development
:cp frontend/.env.development.dist frontend/.env.development
Sign up atReCAPTCHA with thefollowing configurations:
Field Value Label Short
reCAPTCHA type reCAPTCHAv3
Domains localhost
Open
settings
. CopySITE KEY
andSECRET KEY
.Replace the value of
RECAPTCHA_SECRET
in thebackend/.env
file withSECRET KEY
.Replace the value of
REACT_APP_RECAPTCHA_SITE_KEY
infrontend/.env.development
file withSITE KEY
.
Create a new Client ID atGoogle API Credentials:
Click on
Create Credentials
and selectOAuth client ID
.Select
Web application
forApplication type
.Fill in
http://localhost/oauth/google/sign-in/callback
forAuthorized redirect URIs
and click onCreate
.Replace the value of
GOOGLE_CLIENT_ID
inbackend/.env
file withYour Client ID
.Replace the value of
GOOGLE_CLIENT_SECRET
inbackend/.env
file withYour Client Secret
.
You can find the detailed instructions on setting up Facebook sign inhere in case you are interested in.
You can find the detailed instructions on setting up Github sign inhere in case you are interested in.
Update placeholder values with your own configurations.
Launch backend server
cd backend./scripts/dev
Remember to install developers tools before start coding:
./scripts/tools
Update
REACT_APP_RECAPTCHA_SITE_KEY
infrontend/.env.development
.Launch frontend server
cd frontend./scripts/dev
Short backend is built on top ofUncle Bob's Clean Architecture, the centralobjective of which is separation of concerns.
It enables the developers to modify a single component of the system at a timewhile leaving the rest unchanged. This minimizes the amount of changes have tobe made in order to support new requirements as the system grows. CleanArchitecture also improves the testability of system, which in turn savesprecious time when creating automated tests.
Short adoptsMicroservices Architecture toorganize dependent services around business capabilities and to enableindependent deployment of each service.
SSR,Toggle,Status Page, Search,Data Reporter,Feedback Widget,and Cloud API are still under active development.
Short leverages class design, package cohesion, and package coupling principlesto manage logical dependency between internal components.
Principal | Description |
---|---|
Single Responsibility Principle | A class should have one, and only one, reason to change. |
Open Closed Principle | You should be able to extend a classes behavior, without modifying it. |
Liskov Substitution Principle | Derived classes must be substitutable for their base classes. |
Interface Segregation Principle | Make fine grained interfaces that are client specific. |
Dependency Inversion Principle | Depend on abstractions, not on concretions. |
Principal | Description |
---|---|
Release Reuse Equivalency Principle | The granule of reuse is the granule of release. |
The Common Closure Principle | Classes that change together are packaged together. |
The Common Reuse Principle | Classes that are used together are packaged together. |
Principal | Description |
---|---|
Acyclic Dependencies Principle | The dependency graph of packages must have no cycles. |
Stable Dependencies Principle | Depend in the direction of stability. |
Stable Abstractions Principle | Abstractness increases with stability. |
Short produces flexible and loosely coupled code, by explicitly providingcomponents with all of the dependencies they need.
typeAuthenticatorstruct {tokenizer fw.CryptoTokenizertimer fw.TimertokenValidDuration time.Duration}funcNewAuthenticator(tokenizer fw.CryptoTokenizer,timer fw.Timer,tokenValidDuration time.Duration,)Authenticator {returnAuthenticator{tokenizer:tokenizer,timer:timer,tokenValidDuration:tokenValidDuration, }}
Short also simplifies the management of the big block of order-dependentinitialization code withWire, a compile timedependency injection framework by Google.
funcInjectGraphQlService(namestring,sqlDB*sql.DB,graphqlPath provider.GraphQlPath,secret provider.ReCaptchaSecret,jwtSecret provider.JwtSecret,bufferSize provider.KeyGenBufferSize,kgsRPCConfig provider.KgsRPCConfig,tokenValidDuration provider.TokenValidDuration,) (mdservice.Service,error) {wire.Build(wire.Bind(new(fw.GraphQlAPI),new(graphql.Short)),wire.Bind(new(url.Retriever),new(url.RetrieverPersist)),wire.Bind(new(url.Creator),new(url.CreatorPersist)),wire.Bind(new(repo.UserURLRelation),new(db.UserURLRelationSQL)),wire.Bind(new(repo.URL),new(*db.URLSql)),wire.Bind(new(keygen.KeyGenerator),new(keygen.Remote)),wire.Bind(new(service.KeyFetcher),new(kgs.RPC)),observabilitySet,authSet,mdservice.New,provider.NewGraphGophers,mdhttp.NewClient,mdrequest.NewHTTP,mdtimer.NewTimer,db.NewURLSql,db.NewUserURLRelationSQL,provider.NewRemote,url.NewRetrieverPersist,url.NewCreatorPersist,provider.NewKgsRPC,provider.NewReCaptchaService,requester.NewVerifier,graphql.NewShort, )return mdservice.Service{},nil}
Short employsfeature toggles
to modify system behavior without changing code.UI components controlled by the feature toggles are created inside a centralizedUIFactory
in order to avoid having nestedif
else
statement across thecode base:
// UIFactory.tsxexportclassUIFactory{constructor(privatefeatureDecisionService:IFeatureDecisionService){}publiccreateGoogleSignInButton():ReactElement{if(!this.featureDecisionService.includeGoogleSignButton()){return<div/>;}return(<GoogleSignInButtongoogleSignInLink={this.authService.googleSignInLink()}/>);}publiccreateGithubSignInButton():ReactElement{if(!this.featureDecisionService.includeGithubSignButton()){return<div/>;}return(<GithubSignInButtongithubSignInLink={this.authService.githubSignInLink()}/>);}}
Short also providesIFeatureDecisionService
interface, allowing the developersto switch to dynamic feature toggle backend in the future by simply swappingthe dependency injected.
// FeatureDecision.service.tsexportinterfaceIFeatureDecisionService{includeGithubSignButton():boolean;includeGoogleSignButton():boolean;includeFacebookSignButton():boolean;}
// StaticConfigDecision.service.tsimport{IFeatureDecisionService}from'./FeatureDecision.service';exportclassStaticConfigDecisionServiceimplementsIFeatureDecisionService{includeGithubSignButton():boolean{returnfalse;}includeGoogleSignButton():boolean{returnfalse;}includeFacebookSignButton():boolean{returntrue;}}
// dep.tsexportfunctioninitUIFactory( ...):UIFactory{ ...conststaticConfigDecision=newStaticConfigDecisionService(); ...returnnewUIFactory( ...,staticConfigDecision);}
You can read about the detailed feature toggle design onthis article.
In order to improve the quality and quantity of the website's traffic, Shortincreases its visibility to web search engines through HTML meta tags.
<!-- ./frontend/public/index.html --><title>Short: Free online link shortening service</title><!-- Search Engine Optimization --><metaname="description"content="Short enables people to type less for their favorite web sites"><metaname="robots"content="index, follow"><linkhref="https://short-d.com"rel="canonical">
If you searchshort-d.com
on Google, you should see Short shows up asthe first result:
Short leveragesOpen Graph
tags to control what content shows up inthe summary card when the website is shared on Facebook or LinkedIn:
<!-- ./frontend/public/index.html --><!-- Open Graph --><metaproperty="og:title"content="Short: Free link shortening service"/><metaproperty="og:description"content="Short enables people to type less for their favorite web sites"/><metaproperty="og:image"content="https://short-d.com/promo/small-tile.png"/><metaproperty="og:url"content="https://short-d.com"/><metaproperty="og:type"content="website"/>
Shared on Facebook:
Shared on LinkedIn:
Twitter uses its own meta tags to determine what will show up whenthe website is mentioned in a Tweet:
<!-- Twitter --><metaname="twitter:card"content="summary_large_image"/><metaname="twitter:site"content="@byliuyang11"/><metaname="twitter:title"content="Short: Free link shortening service"/><metaname="twitter:description"content="Short enables people to type less for their favorite web sites"/><metaname="twitter:image"content="https://short-d.com/promo/twitter-card.png"/>
Short is maintained by a small team of talented software engineers workingat Google, Uber, and Vmware as a side project. The team wants to deliver newfeatures faster without sacrificing its quality. Testing ever-increasingamount of features manually soon becomes impossible — unless we wantto spend all our time with manual, repetitive work instead of deliveringworking features.
Test automation is the only way forward.
Please readTesting Strategies in a Microservice Architecturefor a detailed introduction on test strategies.
A unit test exercises the smallest piece of testable software in theapplication to determine whether it behaves as expected.
Run unit tests for backend:
cd backend./scripts/unit-test
- [F]ast: Unit tests should be fast otherwise they will slow downdevelopment & deployment.
- [I]ndependent: Never ever write tests which depend on other test cases.
- [R]epeatable: A repeatable test is one that produces the same resultseach time you run it.
- [S]elf-validating: There must be no manual interpretation of the results.
- [T]imely/[T]horoughly: Unit tests must be included for every pull requestof a new feature and cover edge cases, errors, and bad inputs.
An automated test method should be composed of 3As: Arrange, Act, and Assert.
- [A]rrange: All the data needed for a test should be arranged as partof the test. The data used in a test should not depend on the environmentin which the test is running.
- [A]ct: Invoke the actual method under test.
- [A]ssert: A test method should test for a single logical outcome.
An integration test verifies the communication paths and interactionsbetween components to detect interface defects.
Run integration tests for backend:
cd backend./scripts/integration-test
A component test limits the scope of the exercised software to a portionof the system under test, manipulating the system through internal codeinterfaces and using test doubles to isolate the code under test fromother components.
An integration contract test is a test at the boundary of an externalservice verifying that it meets the contract expected by a consumingservice.
An end-to-end test verifies that a system meets external requirementsand achieves its goals, testing the entire system, from end to end.
Currently, we use continuous delivery to deploy code changes to staging &production environment.
Merging pull request into master branch on Github will automatically deploy thechanges tostaging. Merging frommaster
branchtoproduction
branch will automatically deploy the latest code to the production.
In the future, when after we add enough automated tests, we may migrate tocontinuous deployment instead for faster releases.
You can find the differences between continuous delivery & continuous deploymenthere
Short leveragesKubernetes to automate deployment, scaling,and management of containerized microservices.
Short usesGitOps to configure Kubernetescluster and span up new services.
- Drone: Continuous integrationwritten in Go
- Code Climate: Automated codereview
- ElephantSQL: Managed PostgreSQL service.
Please readCONTRIBUTING.md for details on our codeof conduct, the process for submitting pull requests to us, and our codereview guideline.
Harry Liu -Initial work -byliuyang
As the tech lead of Short, I am responsible for the overall planning, executionand success of complex software solutions to meet users' needs.
I deeply believe in and am striving to achieve the right column of thefollowing diagram:
This project is maintained under MIT license