Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
brandongregoryscott committed Aug 4, 2024
0 parents commit 793ff58
Show file tree
Hide file tree
Showing 96 changed files with 18,440 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .czrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"path": "cz-conventional-changelog"
}
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/*.eslintrc.js
10 changes: 10 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This configuration only applies to the package manager root.
/** @type {import("eslint").Linter.Config} */
module.exports = {
ignorePatterns: ["apps/**", "packages/**"],
extends: ["@repo/eslint-config/library.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};
50 changes: 50 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: build

on:
push:
branches: ["*"]
pull_request:
branches: [main]
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup node 20
uses: actions/setup-node@v4
with:
cache: 'npm'
node-version: 20.15.0

- name: npm ci
run: npm ci

- name: npm run build
run: npm run build

release:
name: semantic-release
runs-on: ubuntu-latest
needs: [build]
if: success() && github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4

- name: Setup node 20
uses: actions/setup-node@v4
with:
cache: 'npm'
node-version: 20.15.0

- name: Install semantic-release
run: npm i -g semantic-release

- name: Release
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: npx semantic-release


38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
node_modules
.pnp
.pnp.js

# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# Testing
coverage

# Turbo
.turbo

# Vercel
.vercel

# Build Outputs
.next/
out/
build
dist
.eslintcache

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Misc
.DS_Store
*.pem
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
4 changes: 4 additions & 0 deletions .husky/prepare-commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

exec < /dev/tty && npx cz --hook || true
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
force=true
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.15.0
10 changes: 10 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"arrowParens": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"jsxSingleQuote": false,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "es5",
"useTabs": false
}
75 changes: 75 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
## Base
FROM node:20-alpine3.19 AS base
ARG AWS_ACCESS_KEY_ID
ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
ARG AWS_SECRET_ACCESS_KEY
ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
WORKDIR /app
RUN apk add aws-cli

# Download the sqlite db from s3
RUN aws s3 cp s3://arthistory-spotify-data/$(aws s3 ls s3://arthistory-spotify-data | sort | tail -n 1 | awk '{print $4}') _spotify-data.db

## Builder
FROM base AS builder
RUN apk update
RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN npm install turbo@2 --global

COPY . .

# Generate a partial monorepo with a pruned lockfile for a target workspace.
# Assuming "api" is the name entered in the project's package.json: { name: "api" }
RUN npx turbo prune api --docker

## Installer
FROM base AS installer
RUN apk update
RUN apk add python3 make gcc g++ libc-dev
WORKDIR /app

COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/package-lock.json ./package-lock.json
RUN npm install

# Build the project
COPY --from=builder /app/out/full/ .
RUN npx turbo run build --filter=api...

## Runner
FROM base AS runner
WORKDIR /app

RUN npm install pm2 --global

# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

COPY --from=builder /app/process.yml ./process.yml
COPY --from=builder /app/package.json ./package.json
COPY --from=installer /app/apps/api/dist ./dist
COPY --from=installer /app/apps/api/build ./build
COPY --from=base /app/_spotify-data.db ./_spotify-data.db

ARG PORT=3001
ENV PORT=${PORT}

ARG CLIENT_ID
ENV CLIENT_ID=${CLIENT_ID}

ARG CLIENT_SECRET
ENV CLIENT_SECRET=${CLIENT_SECRET}

ARG DATABASE_PATH=/app/_spotify-data.db
ENV DATABASE_PATH=${DATABASE_PATH}

ARG POSTHOG_KEY
ENV POSTHOG_KEY=${POSTHOG_KEY}

ENV NODE_ENV=production

CMD pm2 start process.yml && tail -f /dev/null

66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<h1 align="center">arthistory</h1>

<p align="center">
<a href="https://github.com/brandongregoryscott/arthistory/actions">
<img src="https://github.com/brandongregoryscott/arthistory/actions/workflows/build.yml/badge.svg">
</a>
<a href="https://github.com/prettier/prettier">
<img alt="code style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"/>
</a>
<a href="http://www.typescriptlang.org/">
<img alt="TypeScript" src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg"/>
</a>
<a href="http://commitizen.github.io/cz-cli/">
<img alt="Commitizen friendly" src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg"/>
</a>
</p>

Web application for viewing historical Spotify artist data.

## Quick Start

The app can be accessed at [arthistory.brandonscott.me](https://arthistory.brandonscott.me).

## About

The Spotify API provides the current follower count and a popularity score for an artist, but does not provide any historical data. I always wanted to track how my favorite artists were growing over time, so maybe you'll find this app useful too.

## How it works

Artist data is synced and pushed everyday to the [spotify-data](https://github.com/brandongregoryscott/spotify-data) repo. To easily query the data over time, a SQLite database is built by iterating through the git history and pushed up to S3, which is pulled down and bundled with the API server.

Not every artist on Spotify is tracked, but new artists can be requested through the web UI or by opening up a pull request to the [spotify-data](https://github.com/brandongregoryscott/spotify-data) repo.

## Development

### Database

The API requires at least a partial SQLite database to query. See the [spotify-data](https://github.com/brandongregoryscott/spotify-data/tree/main?tab=readme-ov-file#building-a-sqlite-database) repo for instructions on how to build a SQLite database to use.

### Spotify API keys

The search endpoint in the API hits the Spotify API, so you'll need API keys to search for artists. See the [Spotify API](https://developer.spotify.com/documentation/web-api/tutorials/getting-started) documentation on signing up for developer access.

### Setup

```sh
# Edit the environment file to add your Spotify API keys and path to the SQLite database file
cp apps/api/.env.example apps/api/.env

# The environment file for the web app probably doesn't need to be changed.
cp apps/web/.env.example apps/web/.env

# Install packages (ensure you are using Node v20+, run `nvm use` if you have `nvm` installed.)
npm i

# Run the development servers for the web app and API
npm run dev

# Now, open http://localhost:3000 in your browser to view the app.
```

## Issues

If you find a bug, feel free to [open up an issue](https://github.com/brandongregoryscott/arthistory/issues/new) and try to describe it in detail with reproduction steps if possible.

If you would like to see a feature, and it isn't [already documented](https://github.com/brandongregoryscott/arthistory/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement), feel free to open up a new issue and describe the desired behavior.
4 changes: 4 additions & 0 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PORT=3001
CLIENT_ID=abc
CLIENT_SECRET=xyz
DATABASE_PATH=~/spotify-data/output/_spotify-data.db
11 changes: 11 additions & 0 deletions apps/api/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
root: true,
extends: [
"@brandongregoryscott/eslint-config",
"@brandongregoryscott/eslint-config/typescript",
],
parserOptions: {
tsconfigRootDir: __dirname,
project: "tsconfig.json",
},
};
38 changes: 38 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"dependencies": {
"@repo/common": "*",
"@spotify/web-api-ts-sdk": "1.2.0",
"body-parser": "1.20.2",
"cors": "2.8.5",
"date-fns": "3.6.0",
"dotenv": "16.3.1",
"esbuild": "0.18.18",
"express": "5.0.0-beta.3",
"express-rate-limit": "6.8.1",
"lodash": "4.17.21",
"posthog-js": "1.144.2",
"sqlite": "5.1.1",
"sqlite3": "5.1.7"
},
"devDependencies": {
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"@types/cors": "2.8.13",
"@types/express": "4.17.17",
"@types/lodash": "4.14.195",
"@types/node": "20.4.2",
"concurrently": "8.2.0",
"nodemon": "3.0.1",
"typescript": "5.5.3"
},
"name": "api",
"private": true,
"scripts": {
"build": "esbuild src/server.ts --bundle --platform=node --target=node20 --outfile=dist/server.js",
"clean": "rm -rf dist",
"dev": "concurrently --names esbuild,nodemon \"npm run build -- --watch\" \"nodemon -q dist/server.js\"",
"start": "node dist/server.js",
"postbuild": "rm -rf ../../build build && cd ../../node_modules/sqlite3 && npm run rebuild && cd - && cp -r ../../node_modules/sqlite3/build ../.. && cp -r ../../node_modules/sqlite3/build ."
},
"version": "1.0.0"
}
38 changes: 38 additions & 0 deletions apps/api/src/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import posthog from "posthog-js";
import { POSTHOG_KEY } from "./config";
import { DEFAULT_ARTIST_IDS } from "@repo/common";

posthog.init(POSTHOG_KEY);

interface ArtistRequestedProperties {
id: string;
}

const artistRequested = (properties: ArtistRequestedProperties): void => {
posthog.capture("Artist Requested", properties);
};

interface ArtistSelectedProperties {
id: string;
}

const artistSelected = (properties: ArtistSelectedProperties): void => {
const { id } = properties;
if (DEFAULT_ARTIST_IDS.includes(id)) {
return;
}

posthog.capture("Artist Selected", properties);
};

interface SearchQueryEnteredProperties {
query: string;
totalCount: number;
trackedCount: number;
}

const searchQueryEntered = (properties: SearchQueryEnteredProperties): void => {
posthog.capture("Search Query Entered", properties);
};

export { artistRequested, artistSelected, searchQueryEntered };
Loading

0 comments on commit 793ff58

Please sign in to comment.