Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
FROM node:12.18.2 as build

FROM oven/bun:1.3.9 AS build



WORKDIR /app

COPY ./package.json /app/package.json
COPY ./package-lock.json /app/package-lock.json
COPY package.json ./
COPY bun.lock ./

RUN npm install
RUN bun install
COPY . .
RUN bun run build

RUN npm run build

FROM golang:latest
FROM golang:tip-alpine3.22

WORKDIR /go/src/app
COPY --from=build /app/public public
COPY server/go.mod .
COPY server/go.sum .
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY server .
COPY . .


EXPOSE 5000
RUN go build -o rest-server main.go
RUN go build -o rest-server cmd/server/main.go
CMD ["./rest-server"]
366 changes: 366 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

165 changes: 165 additions & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package main

import (
"encoding/base64"
"encoding/json"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"

_ "github.com/joho/godotenv/autoload"
)

type body struct {
State string `json:"state"`
}

func authorize(w http.ResponseWriter, req *http.Request) {

isDev := os.Getenv("DEVELOPMENT") == "1"
log.Println(isDev)

if req.Method != http.MethodPost {
w.WriteHeader(http.StatusNotFound)
return
}

w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
if isDev {
w.Header().Set("Access-Control-Allow-Origin", "*")
}

baseUrl := os.Getenv("BASE_URL")
clientId := os.Getenv("CLIENT_ID")
scopes := os.Getenv("SCOPES")
redirectUri := os.Getenv("REDIRECT_URL")
responseType := os.Getenv("RESPONSE_TYPE")

var b body
err := json.NewDecoder(req.Body).Decode(&b)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
log.Println(b)
authUrl := baseUrl + "/authorize" + "?client_id=" + clientId + "&redirect_uri=" + redirectUri + "&scope=" + scopes + "&response_type=" + responseType + "&state=" + b.State
http.Redirect(w, req, authUrl, http.StatusFound)
// log.Println((authUrl))
// w.Write([]byte(authUrl))
}

func callback(w http.ResponseWriter, req *http.Request) {
queryParams := req.URL.Query()
state := queryParams.Get("state")
log.Println("state:", state)
code := queryParams.Get("code")
log.Println("code:", code)

clientId := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
redirectUri := os.Getenv("REDIRECT_URL")
isDev := os.Getenv("DEVELOPMENT") == "1"

tokenResp, err := requestToken(code, redirectUri, clientId, clientSecret)
if err != nil {
log.Println(err)
http.Error(w, "token request failed", http.StatusInternalServerError)
return
}
log.Println(tokenResp)
accessToken, _ := tokenResp["access_token"].(string)
// refreshToken, _ := tokenResp["refresh_token"].(string)
// scope, _ := tokenResp["scope"].(string)
var expiresIn int
if v, ok := tokenResp["expires_in"].(float64); ok {
expiresIn = int(v)
} else {
expiresIn = 3600
}
accessCookie := &http.Cookie{
Name: "access_token",
Value: accessToken,
Path: "/",
MaxAge: expiresIn,
Secure: !isDev,
HttpOnly: false,
SameSite: http.SameSiteLaxMode,
}
http.SetCookie(w, accessCookie)
// scopeCookie := &http.Cookie{
// Name: "scope",
// Value: scope,
// Path: "/",
// MaxAge: expiresIn,
// Secure: !isDev,
// HttpOnly: false,
// SameSite: http.SameSiteLaxMode,
// }
// http.SetCookie(w, scopeCookie)
// if refreshToken != "" {
// refreshCookie := &http.Cookie{
// Name: "refresh_token",
// Value: refreshToken,
// Path: "/",
// // keep refresh longer (e.g. 30 days) or use an env override
// MaxAge: 30 * 24 * 60 * 60,
// Secure: !isDev,
// HttpOnly: false,
// SameSite: http.SameSiteLaxMode,
// }
// http.SetCookie(w, refreshCookie)
// }

http.Redirect(w, req, "/", http.StatusFound)

}

// requestToken exchanges an authorization code for tokens from Spotify.
func requestToken(code, redirectURI, clientID, clientSecret string) (map[string]interface{}, error) {
data := url.Values{}
data.Set("code", code)
data.Set("redirect_uri", redirectURI)
data.Set("grant_type", "authorization_code")

req, err := http.NewRequest(http.MethodPost, "https://accounts.spotify.com/api/token", strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
auth := base64.StdEncoding.EncodeToString([]byte(clientID + ":" + clientSecret))
req.Header.Set("Authorization", "Basic "+auth)

client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return result, nil
}

func main() {

http.Handle("/", http.FileServer(http.Dir("./public")))
http.HandleFunc("/authorize", authorize)
http.HandleFunc("/callback", callback)
// http.HandleFunc("/login", login)

port := os.Getenv("PORT")
log.Println("Starting server on PORT:" + port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rollup -c",
"build": "rollup -c && cp -r public/ server-v2/public",
"dev": "rollup -c -w",
"start": "sirv public --no-clear",
"validate": "svelte-check"
Expand Down
7 changes: 0 additions & 7 deletions public/deps/bootstrap.bundle.min.js

This file was deleted.

1 change: 0 additions & 1 deletion public/deps/bootstrap.bundle.min.js.map

This file was deleted.

7 changes: 0 additions & 7 deletions public/deps/bootstrap.min.css

This file was deleted.

1 change: 0 additions & 1 deletion public/deps/bootstrap.min.css.map

This file was deleted.

61 changes: 0 additions & 61 deletions public/deps/plotly.min.js

This file was deleted.

Binary file removed public/favicon.ico
Binary file not shown.
Binary file removed public/favicon.png
Binary file not shown.
63 changes: 0 additions & 63 deletions public/global.css

This file was deleted.

19 changes: 0 additions & 19 deletions public/index.html

This file was deleted.

4 changes: 4 additions & 0 deletions server-v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# deps
node_modules/
public/
.env
11 changes: 11 additions & 0 deletions server-v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
To install dependencies:
```sh
bun install
```

To run:
```sh
bun run dev
```

open http://localhost:3000
28 changes: 28 additions & 0 deletions server-v2/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions server-v2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "server-v2",
"scripts": {
"dev": "bun run --hot src/index.ts"
},
"dependencies": {
"hono": "^4.12.1",
"zod": "^4.3.6"
},
"devDependencies": {
"@types/bun": "latest"
}
}
Loading