├── .all-contributorsrc
├── .gcloudignore
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── workflow_templates
│ └── template_pushMaster.yml
└── workflows
│ ├── appleauth_pushMaster.yml
│ ├── createappledevtoken_pushMaster.yml
│ ├── createprovideruser_pushMaster.yml
│ ├── createsocialplaylist_pushMaster.yml
│ ├── createuser_pushMaster.yml
│ ├── doesuserdocexist_pushMaster.yml
│ ├── fetchallmedia_pushMaster.yml
│ ├── getapplemusicmedia_pushMaster.yml
│ ├── getspotifymedia_pushMaster.yml
│ ├── socialplatform_pushMaster.yml
│ ├── socialtokenrefresh_pushMaster.yml
│ ├── spotifyauth_pushMaster.yml
│ ├── tokengen_pushMaster.yml
│ ├── updatealgolia_pushMaster.yml
│ └── usernameavailable_pushMaster.yml
├── .gitignore
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE
├── README-Support
├── CONTRIBUTING.md
├── DEPLOYMENT_STATUS.md
├── FAQ.md
└── FUNCTIONS.md
├── README.md
├── cmd
├── appleauth
│ ├── .deployment
│ ├── .version
│ ├── appleauth.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ ├── templates
│ │ └── auth.html.tmpl
│ └── types.go
├── createappledevtoken
│ ├── .deployment
│ ├── .version
│ ├── createappledevtoken.go
│ ├── go.mod
│ ├── go.sum
│ └── helpers.go
├── createprovideruser
│ ├── .deployment
│ ├── .version
│ ├── createprovideruser.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── types.go
├── createsocialplaylist
│ ├── .deployment
│ ├── .version
│ ├── createsocialplaylist.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── types.go
├── createuser
│ ├── .deployment
│ ├── .version
│ ├── createuser.go
│ ├── go.mod
│ ├── go.sum
│ └── helpers.go
├── doesuserdocexist
│ ├── .deployment
│ ├── .version
│ ├── doesuserdocexist.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── types.go
├── fetchallmedia
│ ├── .deployment
│ ├── .version
│ ├── fetchallmedia.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── types.go
├── getapplemusicmedia
│ ├── .deployment
│ ├── .version
│ ├── getapplemusicmedia.go
│ ├── go.mod
│ ├── helpers.go
│ └── types.go
├── getspotifymedia
│ ├── .deployment
│ ├── .version
│ ├── getspotifymedia.go
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── types.go
├── socialplatform
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── socialplatform.go
├── socialtokenrefresh
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ ├── socialtokenrefresh.go
│ └── types.go
├── spotifyauth
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── spotifyauth.go
├── tokengen
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ └── tokengen.go
├── updatealgolia
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ ├── types.go
│ └── updatealgolia.go
└── usernameavailable
│ ├── .deployment
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ ├── helpers.go
│ ├── types.go
│ └── usernameavailable.go
├── go.mod
├── go.sum
├── internal
├── .gitkeep
├── helpers
│ └── localcloudtrigger
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── localcloudtrigger.go
└── mock-config.yaml
├── main.go
├── pkg
├── firebase
│ ├── .version
│ ├── firebase.go
│ ├── go.mod
│ └── go.sum
├── mediahelpers
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ └── mediahelpers.go
├── sawmill
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ └── sawmill.go
└── social
│ ├── .version
│ ├── go.mod
│ ├── go.sum
│ └── social.go
└── scripts
└── gorun.sh
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "adilanchian",
10 | "name": "Alec Dilanchian",
11 | "avatar_url": "https://avatars0.githubusercontent.com/u/13204620?v=4",
12 | "profile": "https://github.com/adilanchian",
13 | "contributions": [
14 | "code",
15 | "maintenance",
16 | "doc"
17 | ]
18 | },
19 | {
20 | "login": "edburtnieks",
21 | "name": "Edgar Burtnieks",
22 | "avatar_url": "https://avatars0.githubusercontent.com/u/47947787?v=4",
23 | "profile": "https://edburtnieks.me",
24 | "contributions": [
25 | "code"
26 | ]
27 | },
28 | {
29 | "login": "isabellabrookes",
30 | "name": "Isabella Brookes",
31 | "avatar_url": "https://avatars1.githubusercontent.com/u/12928252?v=4",
32 | "profile": "https://github.com/isabellabrookes",
33 | "contributions": [
34 | "doc",
35 | "maintenance"
36 | ]
37 | },
38 | {
39 | "login": "LeviHarrison",
40 | "name": "Levi Harrison",
41 | "avatar_url": "https://avatars3.githubusercontent.com/u/54278938?v=4",
42 | "profile": "https://github.com/LeviHarrison",
43 | "contributions": [
44 | "code"
45 | ]
46 | },
47 | {
48 | "login": "brettski",
49 | "name": "Brett Slaski",
50 | "avatar_url": "https://avatars3.githubusercontent.com/u/473633?v=4",
51 | "profile": "http://blog.brettski.com",
52 | "contributions": [
53 | "code",
54 | "doc"
55 | ]
56 | },
57 | {
58 | "login": "Hexnaught",
59 | "name": "Dan",
60 | "avatar_url": "https://avatars2.githubusercontent.com/u/1098786?v=4",
61 | "profile": "https://github.com/Hexnaught",
62 | "contributions": [
63 | "doc"
64 | ]
65 | }
66 | ],
67 | "contributorsPerLine": 7,
68 | "projectName": "gruveebackend",
69 | "projectOwner": "PixelogicDev",
70 | "repoType": "github",
71 | "repoHost": "https://github.com",
72 | "skipCi": true
73 | }
74 |
--------------------------------------------------------------------------------
/.gcloudignore:
--------------------------------------------------------------------------------
1 | # This file specifies files that are *not* uploaded to Google Cloud Platform
2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of
3 | # "#!include" directives (which insert the entries of the given .gitignore-style
4 | # file at that point).
5 | #
6 | # For more information, run:
7 | # $ gcloud topic gcloudignore
8 | #
9 | .gcloudignore
10 | # If you would like to upload your .git directory, .gitignore file or files
11 | # from your .gitignore file, remove the corresponding line
12 | # below:
13 | .git
14 | .gitignore
15 |
16 | node_modules
17 | #!include:.gitignore
18 |
19 | go.mod
20 | go.sum
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | 1. Go to '...'
17 | 1. Click on '....'
18 | 1. Scroll down to '....'
19 | 1. See error
20 |
21 | **Expected behavior**
22 | A clear and concise description of what you expected to happen.
23 |
24 | **Screenshots**
25 | If applicable, add screenshots to help explain your problem.
26 |
27 | **Desktop (please complete the following information):**
28 |
29 | - OS: [e.g. iOS]
30 | - Browser [e.g. chrome, safari]
31 | - Version [e.g. 22]
32 |
33 | **Smartphone (please complete the following information):**
34 |
35 | - Device: [e.g. iPhone6]
36 | - OS: [e.g. iOS8.1]
37 | - Browser [e.g. stock browser, safari]
38 | - Version [e.g. 42]
39 |
40 | **Additional context**
41 | Add any other context about the problem here.
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflow_templates/template_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: [function name] function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/[function name]/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/[function name]"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/appleauth_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: appleauth function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/appleauth/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/appleauth"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/createappledevtoken_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: createappledevtoken function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/createappledevtoken/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/createappledevtoken"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/createprovideruser_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: createprovideruser function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/createprovideruser/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/createprovideruser"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/createsocialplaylist_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: createsocialplaylist function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/createsocialplaylist/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/createsocialplaylist"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/createuser_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: createuser function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/createuser/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/createuser"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/doesuserdocexist_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: doesuserdocexist function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/doesuserdocexist/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/doesuserdocexist"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/fetchallmedia_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: fetchallmedia function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/fetchallmedia/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/fetchallmedia"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
--------------------------------------------------------------------------------
/.github/workflows/getapplemusicmedia_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: getapplemusicmedia function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/getapplemusicmedia/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/getapplemusicmedia"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
--------------------------------------------------------------------------------
/.github/workflows/getspotifymedia_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: getspotifymedia function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/getspotifymedia/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/getspotifymedia"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/socialplatform_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: socialplatform function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/socialplatform/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/socialplatform"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/socialtokenrefresh_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: socialtokenrefresh function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/socialtokenrefresh/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/socialtokenrefresh"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/spotifyauth_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: spotifyauth function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/spotifyauth/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/spotifyauth"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/tokengen_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: tokengen function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/tokengen/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/tokengen"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/updatealgolia_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: updatealgolia function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/updatealgolia/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/updatealgolia"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.github/workflows/usernameavailable_pushMaster.yml:
--------------------------------------------------------------------------------
1 | name: usernameavailable function push master
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'cmd/usernameavailable/**'
9 |
10 | jobs:
11 | tag:
12 | name: Tag Master then Deploy
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Set Function Vars
16 | run: |
17 | echo "::set-env name=FUNC_PATH::cmd/usernameavailable"
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Get Version
23 | run: |
24 | echo "shell is $SHELL"
25 | cd $FUNC_PATH
26 | pwd
27 | # parse version out of .version file
28 | VLINE=$(head -n 1 .version)
29 | echo "Line retrieved from .version: $VLINE"
30 | VERSION=$(echo "$VLINE" | awk -F':' '{print $1}' | egrep -o "v.*")
31 | echo "The found version is $VERSION"
32 | if [ -z $VERSION ]; then exit 99; fi
33 | echo "::set-env name=NEWVER::$VERSION"
34 | - name: Dump vars
35 | run: |
36 | echo "FUNC_PATH: $FUNC_PATH"
37 | echo "NEWVER: $NEWVER"
38 | echo "github ref: $GITHUB_REF"
39 | echo "merge sha $GITHUB_SHA"
40 | - name: Tag Commit
41 | run: |
42 | NEW_TAG="refs/tags/$FUNC_PATH/$NEWVER"
43 | echo "creating tag $NEW_TAG on sha: $GITHUB_SHA"
44 | STATUS_CODE=$(curl -o ./response.txt -w "%{http_code}" -X POST --url https://api.github.com/repos/PixelogicDev/gruveebackend/git/refs \
45 | -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
46 | -H 'content-type: application/json' \
47 | -d "{ \"ref\": \"$NEW_TAG\", \"sha\": \"$GITHUB_SHA\" }")
48 | if [ "$?" != "0" ]; then echo "error running curl"; exit 19; fi
49 | if [ "$STATUS_CODE" == "422" ]; then echo "Status 422, probably duplicate tag!!"; exit 22; fi
50 | if [ "$STATUS_CODE" != "201" ]; then echo "non-201 Create, status: $STATUS_CODE"; exit 201; fi
51 | echo "response of request:"
52 | cat ./response.txt
53 | - name: Deploy prep
54 | run: |
55 | pwd
56 | echo -n $CONFIG_YAML_64 | base64 -d > ./internal/config.yaml
57 | echo "config.yaml character count: $(wc -c < ./internal/config.yaml)"
58 | env:
59 | CONFIG_YAML: ${{ secrets.PROD_CONFIG_YAML }}
60 | CONFIG_YAML_64: ${{ secrets.PROD_CONFIG_YAML_64 }}
61 | - name: Setup gcloud Actions
62 | uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
63 | with:
64 | service_account_key: ${{ secrets.PROD_GCLOUD_AUTH }}
65 | project_id: ${{ secrets.PROD_GCP_PROJECT_ID }}
66 | - name: Run GCP Deploy Function
67 | run: |
68 | cd $FUNC_PATH
69 | pwd
70 | ls -lasi ../../internal
71 | sh ./.deployment
72 | env:
73 | CLOUDSDK_CORE_PROJECT: ${{ secrets.PROD_GCP_PROJECT_ID }}
74 |
75 |
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Config
2 | **/adminSdkSecret.json
3 | **/adminSdkSecret-Dev.json
4 |
5 | # Vendor Folder
6 | **/vendor
7 |
8 | # Enviornment Files
9 | config.yaml
10 |
11 | # Apple .p8 file
12 | cmd/createappledevtoken/*.p8
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["golang.Go", "esbenp.prettier-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.useTabs": true,
3 | "go.autocompleteUnimportedPackages": true,
4 | "go.useCodeSnippetsOnFunctionSuggest": true,
5 | "go.lintTool": "golint",
6 | "go.toolsEnvVars": {
7 | "GO111MODULE": "on"
8 | },
9 | "terminal.integrated.env.osx": {
10 | "GO111MODULE": "on"
11 | },
12 | "terminal.integrated.env.windows": {
13 | "GO111MODULE": "on"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Pixelogic
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-Support/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## The Mantra
4 |
5 | We are great believers in open source and the great community that it builds, we feel that is anyone's right to participate in the development of Grüvee, this means everyone should have equal opportunity to contribute to the awesome world of open source, and at the same time be treated with the highest levels of respect.
6 |
7 | At the end of the day we're all here to build something awesome, so let's go ahead and make something awesome!
8 |
9 | ## 🎉 How to Contribute
10 |
11 | There are multiple ways to contribute to Grüvee, you don't even have to write a single line of code, you can help out by submitting issues, suggesting new features, writing and improving on documentation, fixing bugs or even developing new features.
12 |
13 | ### 🐛 Issues and 🎁 Features Requests
14 |
15 | The only way this project will keep growing and getting better is by all of us chipping in to log bugs and suggesting new features! Please utilize [GitHub Issues](https://github.com/PixelogicDev/gruveebackend/issues) to report any bugs or add suggestions!
16 |
17 | Don't be scared to join the [PixelogicDev discord](https://discord.gg/ubgX6T8) if you need help.
18 |
19 | ### 🔀 Pull Requests
20 |
21 | Now that you are up and running, it's time to push your incredible changes to the Grüvee Repo! For this we will utilize [Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).
22 |
23 | Make sure all your changes are committed to your forked version of Grüvee and go to [the Grüvee repository](https://github.com/PixelogicDev/Gruvee) and create a Pull Request to merge into `master`.
24 |
25 | Leave a brief description of your changes, any images/videos of what your new code is doing (if applicable), and any associated GitHub issues. Get ready for some feedback and thank you for contributing!
26 |
27 | > One last thing to note here: if you are a first time contributor and you are not currently on the [Current Contributors List](#current-contributors), please make sure to include a change with adding yourself! The format is as follows:
28 | >
29 | > ```text
30 | > [DesiredName](linkToYourSocial) - Where did you come from?
31 | > ```
32 |
33 | ### ⭐ Current Contributors
34 |
35 | **Be the first!**
36 |
--------------------------------------------------------------------------------
/README-Support/DEPLOYMENT_STATUS.md:
--------------------------------------------------------------------------------
1 | # Deployment Status
2 |
3 | ## Grüvee Functions GitHub Actions Deployment Status
4 |
5 | 
6 | 
7 | 
8 | 
9 | 
10 | 
11 | 
12 | 
13 | 
14 | 
15 | 
16 | 
17 | 
18 |
--------------------------------------------------------------------------------
/README-Support/FAQ.md:
--------------------------------------------------------------------------------
1 | # Grüvee Backend FAQ
2 |
3 | Here we will list information about some of the inner workings of Grüvee Backend
4 |
5 | ### 🔥 Adding New Firebase Functions
6 |
7 | This project is ordered around Firebase Functions. Each module inside the `cmd` represents it's own Firebase Function.
8 |
9 | #### Updating Build Script
10 |
11 | When adding new functions to this project you will need to also update the build script. This is the process to do that:
12 |
13 | 1. Add a variable for the function replace path. For example if your new function is called `addCoolPerson` create a replace variable that looks like this: `addCoolPerson=github.com/pixelogicdev/gruveebackend/cmd/addcoolperson=../cmd/addcoolperson`
14 | 2. Add a new if statement for `addcoolperon/` directory in the if logic
15 | 3. Make sure to add the new `go mod edit -replace $addCoolPerson` to all other functions (for now it's easier this way to make sure we aren't missing anything)
16 |
17 | We are looking for a much better way to do this, but for now this allows us to use our local changes when developing.
18 |
19 | #### Adding Deployment File & Version File
20 |
21 | You will notice in every function folder we have a `.deployment` and `.version` file:
22 |
23 | `.deployment` - This file includes the script we need to run in order to deploy this function to the cloud and actually utilize is
24 |
25 | `.version` - This file allows us to keep track of the changes in each function and keeps our tags in sync with what is currently in master and what is being developed. Version lines have the following format: `- v1.0.0-beta.1: short description`
26 |
27 | ### 🔀 Merge Process
28 |
29 | > **NOTE: Tagging and deployments are handled during a CI process triggered on push to master. See [Actions Tagging and Deployment](../README.md#auto-tagging-and-deploy-with-github-actions).
30 |
31 | Golang goes off of version numbers in Github. In order for our Firebase functions in the cloud to work properly we need to make sure they download the latest version of each of these functions from this repo. When ready to merge new changes into master we need to do the following:
32 |
33 | 1. Before merging into `master` we need to make sure to go into every function within `/cmd` and remove the replace tags from `go.mod`
34 | 1. Then commit the changes and verify things work locally
35 | 1. Make sure to update the `.version` file in whatever module the change was made in. Follow the versioning system and add a short description of your changes in that file.
36 | 1. Create a Pull Request to merge into `master`
37 | 1. Once merged into `master` we need to tag the module that was changed (We are using this format: `v1.0.0-beta.{WhateverNumberComesNext}`.)
38 | 1. This should happen on the `master` branch so make sure to pull the latest and start the tagging process
39 | 1. The tag needs to happen on the module like so: `cmd/{ModuleName}/{NewVersionNumber} ([Please use this README for reference](https://github.com/go-modules-by-example/index/blob/master/009_submodules/README.md))
40 | 1. Once all the tags have been added, use `git push origin --tags` to push all the tags to master
41 | 1. Verify that the module is not being used by any other modules. If it is, make sure to make another Pull Request to `master` with this change.
42 |
43 | ### 🛫 Deploying Firebase Functions
44 |
45 | After all the tagging and merging into master is good to go, we are ready to deploy to Firebase! The process for this is as follows:
46 |
47 | 1. Change `config.yaml` file `ENVIRONMENT: PROD`
48 | 1. cd into cmd/function folder
49 | 1. In all the functions that need to be redeployed, head over to the `.deployment` and copy the script. It should look something like this
50 |
51 | ```shell
52 | gcloud functions deploy {whatYourHTTPFunctionWillBeCalled} \
53 | --entry-point {ActualClassEntryPoint} \
54 | --runtime go113 \
55 | --trigger-http
56 | --allow-unauthenticated
57 | --env-vars-file ../..internal/config.yaml
58 | ```
59 |
60 | OR
61 |
62 | ```shell
63 | gcloud functions deploy whatYourTriggerFunctionWillBeCalled \
64 | --runtime go113 \
65 | --trigger-event providers/cloud.firestore/eventTypes/document.create \
66 | --trigger-resource "projects/{FirebaseId}/databases/(default)/{PathToCollection}" \
67 | --env-vars-file ../../internal/config.yaml
68 | ```
69 |
70 | ### 🖌 WIPS
71 |
72 | ### Test Firebase Function Trigger Events Locally
73 |
74 | 1. In `internal/helpers/localCloudTrigger` we had an endpoint that creates a new cloud event that points to a specific cloud trigger.
75 | 1. Using something like Insomnia, trigger the localCloudTrigger endpoint with a `FirestoreEvent` payload.
76 | 1. That gets fired off and will "create" a trigger event for the endpoint you pass to it
--------------------------------------------------------------------------------
/cmd/appleauth/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy authorizeWithApple \
2 | --entry-point AuthorizeWithApple \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/appleauth/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.8: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.7: Adding more logs & separating out types and helpers
3 | - v1.0.0-beta.6: Fixing merge
4 | - v1.0.0-beta.5: Reving Fireabse package to the latest
5 | - v1.0.0-beta.4: Removing GetAppleDeveloper token to Firebase pkg
6 | - v1.0.0-beta.3: Tweaking template rendering with new .env variables
7 | - v1.0.0-beta.2: Fixing AuthWithSpotify log
8 | - v1.0.0-beta.1: Initializing new AppleAuth function
--------------------------------------------------------------------------------
/cmd/appleauth/appleauth.go:
--------------------------------------------------------------------------------
1 | package appleauth
2 |
3 | import (
4 | "log"
5 | "net/http"
6 |
7 | "cloud.google.com/go/firestore"
8 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
9 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
10 | "github.com/unrolled/render"
11 | )
12 |
13 | // zebcode - "Zebcode Rules 🦸♂️" (04/29/20)
14 | var (
15 | firestoreClient *firestore.Client
16 | logger sawmill.Logger
17 | appleDevToken firebase.FirestoreAppleDevJWT
18 | httpClient *http.Client
19 | hostname string
20 | templatePath string
21 | )
22 |
23 | func init() {
24 | log.Println("AuthorizeWithApple initialized.")
25 | }
26 |
27 | // AuthorizeWithApple will render a HTML page to get the AppleMusic credentials for user
28 | func AuthorizeWithApple(writer http.ResponseWriter, request *http.Request) {
29 | // Initialize
30 | initWithEnvErr := initWithEnv()
31 | if initWithEnvErr != nil {
32 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
33 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
34 | return
35 | }
36 |
37 | // DR_DinoMight: Dammmmn, Apple Really?!?!?! (08/11/20)
38 | appleDevToken, appleDevTokenErr := firebase.GetAppleDeveloperToken()
39 | if appleDevTokenErr != nil {
40 | http.Error(writer, appleDevTokenErr.Error(), http.StatusInternalServerError)
41 | logger.LogErr("GetAppleMusicMedia", appleDevTokenErr, nil)
42 | return
43 | }
44 |
45 | logger.Log("GetAppleDeveloperToken", "AppleDevToken recieved.")
46 |
47 | // Render template
48 | render := render.New(render.Options{
49 | Directory: templatePath,
50 | })
51 | renderErr := render.HTML(writer, http.StatusOK, "auth", appleDevToken)
52 | if renderErr != nil {
53 | http.Error(writer, renderErr.Error(), http.StatusInternalServerError)
54 | logger.LogErr("Render", renderErr, nil)
55 | return
56 | }
57 |
58 | writer.WriteHeader(http.StatusOK)
59 | }
60 |
--------------------------------------------------------------------------------
/cmd/appleauth/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/appleauth
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.2.0
7 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
8 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
9 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
10 | github.com/square/go-jose v2.5.1+incompatible
11 | github.com/unrolled/render v1.0.3
12 | )
13 |
--------------------------------------------------------------------------------
/cmd/appleauth/helpers.go:
--------------------------------------------------------------------------------
1 | package appleauth
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "net/http"
8 | "os"
9 |
10 | "cloud.google.com/go/firestore"
11 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
12 | )
13 |
14 | // initWithEnv takes our yaml env variables and maps them properly.
15 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
16 | func initWithEnv() error {
17 | if os.Getenv("APPLE_TEAM_ID") == "" {
18 | return fmt.Errorf("authorizeWithApple - APPLE_TEAM_ID does not exist")
19 | }
20 |
21 | // Get paths
22 | var currentProject string
23 | if os.Getenv("ENVIRONMENT") == "DEV" {
24 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
25 | hostname = os.Getenv("HOSTNAME_DEV")
26 | templatePath = os.Getenv("APPLE_AUTH_TEMPLATE_PATH_DEV")
27 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
28 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
29 | hostname = os.Getenv("HOSTNAME_PROD")
30 | templatePath = os.Getenv("APPLE_AUTH_TEMPLATE_PATH_PROD")
31 | }
32 |
33 | // Init HTTP Client
34 | httpClient = &http.Client{}
35 |
36 | // Initialize Firestore
37 | client, err := firestore.NewClient(context.Background(), currentProject)
38 | if err != nil {
39 | return fmt.Errorf("AuthorizeWithApple [Init Firestore]: %v", err)
40 | }
41 |
42 | // Initialize Sawmill
43 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "AuthorizeWithApple")
44 | if err != nil {
45 | log.Printf("AuthorizeWithApple [Init Sawmill]: %v", err)
46 | }
47 |
48 | firestoreClient = client
49 | logger = sawmillLogger
50 | return nil
51 | }
52 |
--------------------------------------------------------------------------------
/cmd/appleauth/templates/auth.html.tmpl:
--------------------------------------------------------------------------------
1 | {{ define "auth" }}
2 |
3 |
4 | Apple Music Auth
5 |
6 |
7 |
8 |
9 |
43 |
44 |
45 |
49 |
50 |
60 |
61 |
62 |
63 | {{ end }}
64 |
--------------------------------------------------------------------------------
/cmd/appleauth/types.go:
--------------------------------------------------------------------------------
1 | package appleauth
2 |
3 | // appleDevTokenResp in the token that comes back from a successful Apple Music Auth call
4 | type appleDevTokenResp struct {
5 | Token string
6 | }
7 |
--------------------------------------------------------------------------------
/cmd/createappledevtoken/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy createAppleDevToken \
2 | --entry-point CreateAppleDevToken \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/createappledevtoken/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.6: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.5: Adding more log statements & creating separate helpers file
3 | - v1.0.0-beta.5: Fixing merge
4 | - v1.0.0-beta.4: Updating firebase pkg
5 | - v1.0.0-beta.3: Removed Firebase Bucket logic and added .p8 file and logic
6 | - v1.0.0-beta.2: Changing logic to get .p8 file from Firebase Bucket
7 | - v1.0.0-beta.1: Initial instance of function
--------------------------------------------------------------------------------
/cmd/createappledevtoken/createappledevtoken.go:
--------------------------------------------------------------------------------
1 | package createappledevtoken
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "net/http"
8 | "os"
9 |
10 | "cloud.google.com/go/firestore"
11 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
12 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
13 | )
14 |
15 | // DR_DinoMight - "Alec loves the song "Nelly's - Hot In Here" (05/05/20)
16 | var (
17 | currentProject string
18 | firestoreClient *firestore.Client
19 | logger sawmill.Logger
20 | appleDevToken firebase.FirestoreAppleDevJWT
21 | applePrivateKeyPath string
22 | )
23 |
24 | func init() {
25 | log.Println("CreateAppleDevToken initialized.")
26 | }
27 |
28 | // CreateAppleDevToken will render a HTML page to get the AppleMusic credentials for user
29 | func CreateAppleDevToken(writer http.ResponseWriter, request *http.Request) {
30 | // Initialize
31 | initWithEnvErr := initWithEnv()
32 | if initWithEnvErr != nil {
33 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
34 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
35 | return
36 | }
37 |
38 | // Check for developer token in firebase
39 | devToken, devTokenErr := fetchToken()
40 | if devTokenErr != nil {
41 | http.Error(writer, devTokenErr.Error(), http.StatusInternalServerError)
42 | logger.LogErr("GetAppleDevToken", devTokenErr, nil)
43 | return
44 | }
45 |
46 | logger.Log("GetAppleDevToken", "DevToken received from Firebase")
47 |
48 | // If we have the token check to see if it needs to be refreshed
49 | if devToken != nil {
50 | appleDevToken = *devToken
51 |
52 | logger.Log("CreateAppleDevToken", "Token found in DB. Checking for expiration")
53 |
54 | // If token expired, refresh, else continue
55 | if isTokenExpired(devToken) {
56 | if os.Getenv("APPLE_TEAM_ID") == "" {
57 | http.Error(writer, "[CreateAppleDevToken] APPLE_TEAM_ID does not exist!", http.StatusInternalServerError)
58 | logger.LogErr("RefreshAppleDevToken", fmt.Errorf("APPLE_TEAM_ID does not exist"), nil)
59 | return
60 | }
61 |
62 | if os.Getenv("APPLE_KID") == "" {
63 | http.Error(writer, "[CreateAppleDevToken] APPLE_KID does not exist!", http.StatusInternalServerError)
64 | logger.LogErr("RefreshAppleDevToken", fmt.Errorf("APPLE_KID does not exist"), nil)
65 | return
66 | }
67 |
68 | logger.Log("CreateAppleDevToken", "Apple Team Id & Apple KID are here.")
69 |
70 | token, tokenErr := generateJWT()
71 | if tokenErr != nil {
72 | http.Error(writer, tokenErr.Error(), http.StatusInternalServerError)
73 | logger.LogErr("RefreshAppleDevToken", tokenErr, nil)
74 | return
75 | }
76 |
77 | logger.Log("GenerateJWT", "JWT generated successfully")
78 |
79 | appleDevToken = *token
80 | }
81 |
82 | writer.WriteHeader(http.StatusOK)
83 | writer.Header().Set("Content-Type", "application/json")
84 | json.NewEncoder(writer).Encode(appleDevToken)
85 | return
86 | }
87 |
88 | // If we do not have the token, we need to generate a new one
89 | token, tokenErr := generateJWT()
90 | if tokenErr != nil {
91 | http.Error(writer, tokenErr.Error(), http.StatusInternalServerError)
92 | logger.LogErr("GenerateAppleDevToken", tokenErr, nil)
93 | return
94 | }
95 |
96 | writer.WriteHeader(http.StatusOK)
97 | writer.Header().Set("Content-Type", "application/json")
98 | json.NewEncoder(writer).Encode(token)
99 | }
100 |
--------------------------------------------------------------------------------
/cmd/createappledevtoken/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/createappledevtoken
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go v0.60.0 // indirect
7 | cloud.google.com/go/firestore v1.2.0
8 | cloud.google.com/go/storage v1.10.0 // indirect
9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
10 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
11 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
12 | go.opencensus.io v0.22.4 // indirect
13 | golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
14 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect
15 | golang.org/x/text v0.3.3 // indirect
16 | golang.org/x/tools v0.0.0-20200708003708-134513de8882 // indirect
17 | google.golang.org/grpc v1.30.0
18 | )
19 |
--------------------------------------------------------------------------------
/cmd/createappledevtoken/helpers.go:
--------------------------------------------------------------------------------
1 | package createappledevtoken
2 |
3 | import (
4 | "context"
5 | "crypto/x509"
6 | "encoding/pem"
7 | "fmt"
8 | "io/ioutil"
9 | "log"
10 | "os"
11 | "time"
12 |
13 | "cloud.google.com/go/firestore"
14 | "github.com/dgrijalva/jwt-go"
15 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
16 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
17 | "google.golang.org/grpc/codes"
18 | "google.golang.org/grpc/status"
19 | )
20 |
21 | // Helpers
22 | // initWithEnv takes our yaml env variables and maps them properly.
23 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
24 | func initWithEnv() error {
25 | if os.Getenv("ENVIRONMENT") == "DEV" {
26 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
27 | applePrivateKeyPath = os.Getenv("APPLE_PRIVATE_KEY_PATH_DEV")
28 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
29 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
30 | applePrivateKeyPath = os.Getenv("APPLE_PRIVATE_KEY_PATH_PROD")
31 | }
32 |
33 | // Initialize Firestore
34 | client, err := firestore.NewClient(context.Background(), currentProject)
35 | if err != nil {
36 | return fmt.Errorf("CreateAppleDevToken [Init Firestore]: %v", err)
37 | }
38 |
39 | // Initialize Sawmill
40 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "CreateAppleDevToken")
41 | if err != nil {
42 | log.Printf("CreateAppleDevToken [Init Sawmill]: %v", err)
43 | }
44 |
45 | firestoreClient = client
46 | logger = sawmillLogger
47 | return nil
48 | }
49 |
50 | // fetchToken will grab the Apple Developer Token from DB
51 | func fetchToken() (*firebase.FirestoreAppleDevJWT, error) {
52 | logger.Log("FetchToken", "Starting...")
53 |
54 | // Go to Firebase and see if appleDevToken exists
55 | snapshot, snapshotErr := firestoreClient.Collection("internal_tokens").Doc("appleDevToken").Get(context.Background())
56 | if status.Code(snapshotErr) == codes.NotFound {
57 | logger.Log("FetchToken", "AppleDevToken not found in DB. Need to create.")
58 | return nil, nil
59 | }
60 |
61 | if snapshotErr != nil {
62 | return nil, fmt.Errorf(snapshotErr.Error())
63 | }
64 |
65 | logger.Log("FetchToken", "Snapshot found.")
66 |
67 | var appleDevToken firebase.FirestoreAppleDevJWT
68 | dataToErr := snapshot.DataTo(&appleDevToken)
69 | if dataToErr != nil {
70 | return nil, fmt.Errorf(dataToErr.Error())
71 | }
72 |
73 | logger.Log("FetchToken", "Decoded AppleDevToken successfully.")
74 |
75 | return &appleDevToken, nil
76 | }
77 |
78 | // isTokenExpired will check to see if the Apple Developer Token is expired
79 | func isTokenExpired(token *firebase.FirestoreAppleDevJWT) bool {
80 | logger.Log("IsTokenExpired", "Starting...")
81 |
82 | // Get current time
83 | var currentTime = time.Now()
84 |
85 | logger.Log("IsTokenExpired", fmt.Sprintf("Issued At: %d seconds", token.IssuedAt))
86 | logger.Log("IsTokenExpired", fmt.Sprintf("Expires At: %d seconds\n", token.ExpiresAt))
87 |
88 | if currentTime.After(time.Unix(token.ExpiresAt, 0)) {
89 | logger.Log("IsTokenExpired", "Token is expired. Need to generate a new one.")
90 | return true
91 | }
92 |
93 | logger.Log("IsTokenExpired", "Token is not expired.")
94 | return false
95 | }
96 |
97 | // generateJWT will create a new Apple Developer Token and store in DB
98 | func generateJWT() (*firebase.FirestoreAppleDevJWT, error) {
99 | logger.Log("GenerateJWT", "Starting...")
100 |
101 | // Env Props
102 | appleTeamKey := os.Getenv("APPLE_TEAM_ID")
103 | appleKID := os.Getenv("APPLE_KID")
104 |
105 | // Download .p8 file
106 | signKeyByte, signKeyByteErr := ioutil.ReadFile(applePrivateKeyPath)
107 | if signKeyByteErr != nil {
108 | return nil, fmt.Errorf("Could not read .p8 file: %v", signKeyByteErr)
109 | }
110 |
111 | logger.Log("GenerateJWT", ".p8 file read successfully.")
112 |
113 | // The issued at (iat) registered claim key, whose value indicates the time at which the token was generated, in terms of the number of seconds since Epoch, in UTC
114 | sixMonthsInSec := 15777000
115 | issuedAt := time.Now()
116 | expiresAt := issuedAt.Add(time.Second * time.Duration(sixMonthsInSec))
117 |
118 | // Setup Claims
119 | claims := jwt.StandardClaims{
120 | Issuer: appleTeamKey,
121 | ExpiresAt: int64(expiresAt.Unix()),
122 | // SagNurSchwitzer - "WHO WILL FIX ME NOW" (05/06/20)
123 | IssuedAt: int64(issuedAt.Unix()),
124 | }
125 |
126 | logger.Log("GenerateJWT", "JWT claims setup successfully.")
127 |
128 | // Generate and sign JWT
129 | token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
130 | token.Header = map[string]interface{}{
131 | "alg": "ES256",
132 | "kid": appleKID,
133 | }
134 |
135 | logger.Log("GenerateJWT", "Generated new JWT successfully.")
136 |
137 | // Decode block
138 | block, _ := pem.Decode(signKeyByte)
139 |
140 | logger.Log("GenerateJWT", "Created JWT block successfully.")
141 |
142 | // Create proper key
143 | signingKey, signingKeyError := x509.ParsePKCS8PrivateKey(block.Bytes)
144 | if signingKeyError != nil {
145 | return nil, fmt.Errorf("Could not parse Private key: %v", signingKeyError)
146 | }
147 |
148 | logger.Log("GenerateJWT", "Created JWT signing key successfully.")
149 |
150 | ss, err := token.SignedString(signingKey)
151 | if err != nil {
152 | return nil, fmt.Errorf("Cannot sign token: %v", err)
153 | }
154 |
155 | logger.Log("GenerateJWT", "Created signed string successfully.")
156 |
157 | // Create object
158 | appleDevToken := firebase.FirestoreAppleDevJWT{
159 | ExpiresAt: claims.ExpiresAt,
160 | IssuedAt: claims.IssuedAt,
161 | Token: ss,
162 | }
163 |
164 | // Write dev token to DB
165 | writeError := writeAppleDevToken(appleDevToken)
166 | if writeError != nil {
167 | return nil, writeError
168 | }
169 |
170 | logger.Log("GenerateJWT", "Dev token successfully written to Firestore.")
171 |
172 | return &appleDevToken, nil
173 | }
174 |
175 | // writeAppleDevToken writes the newly created JWT to our database
176 | func writeAppleDevToken(JWT firebase.FirestoreAppleDevJWT) error {
177 | logger.Log("WriteAppleDevToken", "Starting...")
178 |
179 | appleJWTDoc := firestoreClient.Collection("internal_tokens").Doc("appleDevToken")
180 | if appleJWTDoc == nil {
181 | return fmt.Errorf("appleDevToken could not be found")
182 | }
183 |
184 | logger.Log("WriteAppleDevToken", "Received AppleDevtoken from Firestore Collection")
185 |
186 | jwtInterface := map[string]interface{}{
187 | "expiresAt": JWT.ExpiresAt,
188 | "issuedAt": JWT.IssuedAt,
189 | "token": JWT.Token,
190 | }
191 |
192 | _, writeErr := appleJWTDoc.Set(context.Background(), jwtInterface, firestore.MergeAll)
193 | if writeErr != nil {
194 | return fmt.Errorf(writeErr.Error())
195 | }
196 |
197 | logger.Log("WriteAppleDevToken", "Set Apple DevToken in Firestore.")
198 |
199 | return nil
200 | }
201 |
--------------------------------------------------------------------------------
/cmd/createprovideruser/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy createProviderUser \
2 | --entry-point CreateProviderUser \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/createprovideruser/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.4: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.3: Adding more log statements & creating helpers and types files
3 | - v1.0.0-beta.2: Fixing merge
4 | - v1.0.0-beta.1: Initial instance of function
--------------------------------------------------------------------------------
/cmd/createprovideruser/createprovideruser.go:
--------------------------------------------------------------------------------
1 | package createprovideruser
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "net/http"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | var (
14 | firestoreClient *firestore.Client
15 | logger sawmill.Logger
16 | )
17 |
18 | func init() {
19 | log.Println("CreateProviderUser initialized.")
20 | }
21 |
22 | // CreateProviderUser will check to see if the newly created user needs to be added to the providers_users collection
23 | func CreateProviderUser(writer http.ResponseWriter, request *http.Request) {
24 | // Initialize
25 | initErr := initWithEnv()
26 | if initErr != nil {
27 | http.Error(writer, initErr.Error(), http.StatusInternalServerError)
28 | logger.LogErr("InitWithEnv", initErr, nil)
29 | return
30 | }
31 |
32 | // Decode
33 | var reqData createProviderUserReq
34 |
35 | reqDataErr := json.NewDecoder(request.Body).Decode(&reqData)
36 | if reqDataErr != nil {
37 | http.Error(writer, reqDataErr.Error(), http.StatusInternalServerError)
38 | logger.LogErr("ReqData Decoder", reqDataErr, request)
39 | return
40 | }
41 |
42 | logger.Log("CreateProviderUser", "Request was decoded successfully")
43 |
44 | // Create document references
45 | firebaseProviderDocRef := firestoreClient.Doc("provider_users/" + reqData.FirebaseProviderUID)
46 | platformProviderDocRef := firestoreClient.Doc("users/" + reqData.PlatformProviderUID)
47 |
48 | logger.Log("CreateProviderUser", "FirebaseProvider & PlatformProvider docs creates")
49 |
50 | // Create ProviderUser Object
51 | providerUserData := providerUser{
52 | PlatformUserRef: platformProviderDocRef,
53 | }
54 |
55 | // Write to Firestore
56 | _, writeErr := firebaseProviderDocRef.Set(context.Background(), providerUserData)
57 | if writeErr != nil {
58 | http.Error(writer, writeErr.Error(), http.StatusInternalServerError)
59 | logger.LogErr("FireStore Set", writeErr, request)
60 | return
61 | }
62 |
63 | logger.Log("CreateProviderUser", "Write FirebaseProvider was successful.")
64 |
65 | writer.WriteHeader(http.StatusOK)
66 | }
67 |
--------------------------------------------------------------------------------
/cmd/createprovideruser/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/createprovideruser
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.2.0
7 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
8 | )
9 |
--------------------------------------------------------------------------------
/cmd/createprovideruser/helpers.go:
--------------------------------------------------------------------------------
1 | package createprovideruser
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | // Get Project ID
20 | if os.Getenv("ENVIRONMENT") == "DEV" {
21 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
22 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
23 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
24 | }
25 |
26 | // Initialize Firestore
27 | client, err := firestore.NewClient(context.Background(), currentProject)
28 | if err != nil {
29 | return fmt.Errorf("CreateProviderUser [Init Firestore]: %v", err)
30 | }
31 |
32 | // Initialize Sawmill
33 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "CreateProviderUser")
34 | if err != nil {
35 | log.Printf("CreateAppleDevToken [Init Sawmill]: %v", err)
36 | }
37 |
38 | firestoreClient = client
39 | logger = sawmillLogger
40 | return nil
41 | }
42 |
--------------------------------------------------------------------------------
/cmd/createprovideruser/types.go:
--------------------------------------------------------------------------------
1 | package createprovideruser
2 |
3 | import "cloud.google.com/go/firestore"
4 |
5 | // updateProviderUserReq takes in the Firebase Provider UID and the platform provider UID to map
6 | type createProviderUserReq struct {
7 | FirebaseProviderUID string `json:"firebaseProviderUID"`
8 | PlatformProviderUID string `json:"platformProviderUID"`
9 | }
10 |
11 | // providerUser takes the platformUser document reference and stores in new collection
12 | type providerUser struct {
13 | PlatformUserRef *firestore.DocumentRef `firestore:"platformUserReference"`
14 | }
15 |
--------------------------------------------------------------------------------
/cmd/createsocialplaylist/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy createSocialPlaylist \
2 | --entry-point CreateSocialPlaylist \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/createsocialplaylist/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.10: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.9: Adding more logging & creating types and helpers files
3 | - v1.0.0-beta.8: Fixing merge
4 | - v1.0.0-beta.7: Reving firebase pkg and social pkg
5 | - v1.0.0-beta.6: Adding support for Apple Music Playlist creation
6 | - v1.0.0-beta.5: Changing enviornment variable check location
7 | - v1.0.0-beta.4: Changed request body to take playlist name instead of playlist object
8 | - v1.0.0-beta.3: Adding support for new APIToken response to client & reving social pkg
9 | - v1.0.0-beta.2: Reving firebase pkg and social pkg
10 | - v1.0.0-beta.1: Adding ability to create playlist on social platform
--------------------------------------------------------------------------------
/cmd/createsocialplaylist/createsocialplaylist.go:
--------------------------------------------------------------------------------
1 | package createsocialplaylist
2 |
3 | // Dragonfleas - "bobby drop tables wuz here pog - Dragonfleas - Relevant XKCD" (03/23/20)
4 | // HMigo - "EN LØK HAR FLERE LAG" (03/26/20)
5 | import (
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "net/http"
10 |
11 | "cloud.google.com/go/firestore"
12 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
13 | "github.com/pixelogicdev/gruveebackend/pkg/social"
14 | )
15 |
16 | var (
17 | firestoreClient *firestore.Client
18 | logger sawmill.Logger
19 | httpClient *http.Client
20 | hostname string
21 | )
22 |
23 | // ywnklme - "At least something in my life is social 😞" (03/23/20)
24 | func init() {
25 | log.Println("CreateSocialPlaylist Initialized")
26 | }
27 |
28 | // CreateSocialPlaylist will take in a SocialPlatform and will go create a playlist on the social account itself
29 | func CreateSocialPlaylist(writer http.ResponseWriter, request *http.Request) {
30 | // Initialize paths
31 | err := initWithEnv()
32 | if err != nil {
33 | http.Error(writer, err.Error(), http.StatusInternalServerError)
34 | logger.LogErr("InitWithEnv", err, nil)
35 | return
36 | }
37 |
38 | var socialPlaylistReq createSocialPlaylistRequest
39 |
40 | // Decode our object
41 | jsonDecodeErr := json.NewDecoder(request.Body).Decode(&socialPlaylistReq)
42 | if jsonDecodeErr != nil {
43 | http.Error(writer, jsonDecodeErr.Error(), http.StatusInternalServerError)
44 | logger.LogErr("SocialPlaylistReq Decoder", jsonDecodeErr, request)
45 | return
46 | }
47 |
48 | logger.Log("CreateSocialPlaylist", "Decoded request")
49 |
50 | // Figure out what service we are going to create a playlist in
51 | var platformEndpoint string
52 | var socialRefreshTokens *social.RefreshTokensResponse
53 | var socialRefreshTokenErr error
54 |
55 | if socialPlaylistReq.SocialPlatform.PlatformName == "spotify" {
56 | logger.Log("CreateSocialPlaylist", "Creating Spotify Playlist")
57 | platformEndpoint = "https://api.spotify.com/v1/users/" + socialPlaylistReq.SocialPlatform.ID + "/playlists"
58 |
59 | // This is sort of weird, but I haven't been able to find any resources on an Apple Music tokens expiring
60 | // Therefore, this check should only be done on Spotify at the moment
61 | socialRefreshTokens, socialRefreshTokenErr = refreshToken(socialPlaylistReq.SocialPlatform)
62 | if socialRefreshTokenErr != nil {
63 | http.Error(writer, socialRefreshTokenErr.Error(), http.StatusBadRequest)
64 | logger.LogErr("RefreshToken", socialRefreshTokenErr, request)
65 | return
66 | }
67 |
68 | logger.Log("RefreshToken", "Succesfully refreshed tokens.")
69 | }
70 |
71 | if socialPlaylistReq.SocialPlatform.PlatformName == "apple" {
72 | logger.Log("CreateSocialPlaylist", "Creating Apple Music Playlist")
73 | platformEndpoint = "https://api.music.apple.com/v1/me/library/playlists"
74 | }
75 |
76 | if socialPlaylistReq.SocialPlatform.PlatformName == "youtube" {
77 | logger.Log("CreateSocialPlaylist", "Creating YouTube Music Playlist")
78 | }
79 |
80 | // fr3fou - "i fixed this Kappa" (04/10/20)
81 | // Setup resonse if we have a token to return
82 | var response *createSocialPlaylistResponse
83 |
84 | // Again, this is solely for Spotify at the moment
85 | if socialPlaylistReq.SocialPlatform.PlatformName == "spotify" && socialRefreshTokens != nil {
86 | logger.Log("CreateSocialPlaylist", "Spotify token was refreshed for user.")
87 |
88 | // Get token for specified platform
89 | platformRefreshToken, doesExist := socialRefreshTokens.RefreshTokens[socialPlaylistReq.SocialPlatform.PlatformName]
90 | if doesExist == true {
91 | logger.Log("CreateSocialPlaylist", "Setting new APIToken on socialPlatform")
92 | socialPlaylistReq.SocialPlatform.APIToken.Token = platformRefreshToken.Token
93 |
94 | // Write new apiToken as response
95 | response = &createSocialPlaylistResponse{
96 | PlatformName: socialPlaylistReq.SocialPlatform.PlatformName,
97 | RefreshToken: platformRefreshToken,
98 | }
99 | } else {
100 | // Another token needed refresh, but not the one we were looking for
101 | logger.Log("CreateSocialPlaylist", fmt.Sprintf("%s was not refreshed", socialPlaylistReq.SocialPlatform.PlatformName))
102 | log.Printf("%s was not refreshed", socialPlaylistReq.SocialPlatform.PlatformName)
103 | }
104 | }
105 |
106 | // Call API to create playlist with data
107 | createReqErr := createPlaylist(platformEndpoint, socialPlaylistReq.SocialPlatform, socialPlaylistReq.PlaylistName)
108 | if createReqErr != nil {
109 | http.Error(writer, createReqErr.Error(), http.StatusBadRequest)
110 | logger.LogErr("CreatePlaylist", createReqErr, request)
111 | return
112 | }
113 |
114 | logger.Log("CreateSocialPlaylist", "CreatePlaylist call was successful.")
115 |
116 | // If a new token was generated, send back to the client
117 | if response != nil {
118 | json.NewEncoder(writer).Encode(response)
119 | } else {
120 | writer.WriteHeader(http.StatusNoContent)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/cmd/createsocialplaylist/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/createsocialplaylist
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | google.golang.org/grpc v1.28.0
11 | )
12 |
--------------------------------------------------------------------------------
/cmd/createsocialplaylist/types.go:
--------------------------------------------------------------------------------
1 | package createsocialplaylist
2 |
3 | import "github.com/pixelogicdev/gruveebackend/pkg/firebase"
4 |
5 | // createSocialPlaylistRequest includes the socialPlatform and playlist that will be added
6 | type createSocialPlaylistRequest struct {
7 | SocialPlatform firebase.FirestoreSocialPlatform `json:"socialPlatform"`
8 | PlaylistName string `json:"playlistName"`
9 | }
10 |
11 | // createSocialPlaylistResponse includes the refreshToken for the platform if there is one
12 | type createSocialPlaylistResponse struct {
13 | PlatformName string `json:"platformName"`
14 | RefreshToken firebase.APIToken `json:"refreshToken"`
15 | }
16 |
17 | // appleMusicPlaylistRequest includes the payload needed to create an Apple Music Playlist
18 | type appleMusicPlaylistRequest struct {
19 | Attributes struct {
20 | Name string `json:"name"`
21 | Description string `json:"description"`
22 | } `json:"attributes"`
23 | }
24 |
25 | // spotifyPlaylistRequest includes the payload needed to create a Spotify Playlist
26 | type spotifyPlaylistRequest struct {
27 | Name string `json:"name"`
28 | Public bool `json:"public"`
29 | Collaborative bool `json:"collaborative"`
30 | Description string `json:"description"`
31 | }
32 |
--------------------------------------------------------------------------------
/cmd/createuser/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy createUser \
2 | --entry-point CreateUser \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/createuser/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.6: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.5: Adding more logging & adding helpers file
3 | - v1.0.0-beta.4: Fixing merge
4 | - v1.0.0-beta.3: Reving firebase package to latest
5 | - v1.0.0-beta.2: Changing enviornment variable check location
6 | - v1.0.0-beta.1: Reving firebase package to latest
--------------------------------------------------------------------------------
/cmd/createuser/createuser.go:
--------------------------------------------------------------------------------
1 | package createuser
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "net/http"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
11 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
12 | "github.com/pixelogicdev/gruveebackend/pkg/social"
13 | )
14 |
15 | var (
16 | firestoreClient *firestore.Client
17 | logger sawmill.Logger
18 | )
19 |
20 | func init() {
21 | log.Println("CreateUser Initialized")
22 | }
23 |
24 | // CreateUser will write a new Firebase user to Firestore
25 | func CreateUser(writer http.ResponseWriter, request *http.Request) {
26 | // Initialize
27 | initWithEnvErr := initWithEnv()
28 | if initWithEnvErr != nil {
29 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
30 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
31 | return
32 | }
33 |
34 | var createUserReq social.CreateUserReq
35 |
36 | jsonDecodeErr := json.NewDecoder(request.Body).Decode(&createUserReq)
37 | if jsonDecodeErr != nil {
38 | http.Error(writer, jsonDecodeErr.Error(), http.StatusInternalServerError)
39 | logger.LogErr("CreateUserReq Decoder", jsonDecodeErr, request)
40 | return
41 | }
42 |
43 | logger.Log("CreateUser", "Decoded Request successfully.")
44 |
45 | // Get Document references for social platform
46 | socialPlatDocRef := firestoreClient.Doc(createUserReq.SocialPlatformPath)
47 | if socialPlatDocRef == nil {
48 | http.Error(writer, jsonDecodeErr.Error(), http.StatusInternalServerError)
49 | logger.LogErr("CreateUserReq Decoder", jsonDecodeErr, request)
50 | return
51 | }
52 |
53 | logger.Log("CreateUser", "Receieved Social Document Reference successfully.")
54 |
55 | // Create Firestore user
56 | firestoreUser := firebase.FirestoreUser{
57 | Email: createUserReq.Email,
58 | ID: createUserReq.ID,
59 | Playlists: []*firestore.DocumentRef{},
60 | PreferredSocialPlatform: socialPlatDocRef,
61 | ProfileImage: createUserReq.ProfileImage,
62 | SocialPlatforms: []*firestore.DocumentRef{socialPlatDocRef},
63 | DisplayName: createUserReq.DisplayName,
64 | Username: createUserReq.Username,
65 | }
66 |
67 | // Write FirestoreUser to Firestore
68 | _, writeErr := firestoreClient.Collection("users").Doc(firestoreUser.ID).Set(context.Background(), firestoreUser)
69 | if writeErr != nil {
70 | http.Error(writer, writeErr.Error(), http.StatusInternalServerError)
71 | logger.LogErr("FireStore Set", writeErr, nil)
72 | return
73 | }
74 |
75 | logger.Log("CreateUser", "Successfully wrote new data to Firestore.")
76 |
77 | // Return Firestore User
78 | writer.WriteHeader(http.StatusOK)
79 | writer.Header().Set("Content-Type", "application/json")
80 | json.NewEncoder(writer).Encode(firestoreUser)
81 | }
82 |
--------------------------------------------------------------------------------
/cmd/createuser/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/createuser
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | )
11 |
--------------------------------------------------------------------------------
/cmd/createuser/helpers.go:
--------------------------------------------------------------------------------
1 | package createuser
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | if os.Getenv("ENVIRONMENT") == "DEV" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
21 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
22 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
23 | }
24 |
25 | // Initialize Firestore
26 | client, err := firestore.NewClient(context.Background(), currentProject)
27 | if err != nil {
28 | return fmt.Errorf("CreateUser [Init Firestore]: %v", err)
29 | }
30 |
31 | // Initialize Sawmill
32 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "CreateUser")
33 | if err != nil {
34 | log.Printf("CreateSocial Playlist [Init Sawmill]: %v", err)
35 | }
36 |
37 | firestoreClient = client
38 | logger = sawmillLogger
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy doesUserDocExist \
2 | --entry-point DoesUserDocExist \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.4: Fixing malformed go.sum
2 | - v1.0.0-beta.3: Reving to trigger deployment (IGNORE)
3 | - v1.0.0-beta.2: Fixing merge
4 | - v1.0.0-beta.1: Initializing function
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/doesuserdocexist.go:
--------------------------------------------------------------------------------
1 | package doesuserdocexist
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "net/http"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | "google.golang.org/grpc/codes"
12 | "google.golang.org/grpc/status"
13 | )
14 |
15 | var (
16 | firestoreClient *firestore.Client
17 | logger sawmill.Logger
18 | )
19 |
20 | func init() {
21 | log.Println("DoesUserDocExist intialized")
22 | }
23 |
24 | // DoesUserDocExist checks to see if there is already a Firebase user document for someone right before they sign in
25 | func DoesUserDocExist(writer http.ResponseWriter, request *http.Request) {
26 | doesUserDocExist := false
27 |
28 | // Initialize
29 | initWithEnvErr := initWithEnv()
30 | if initWithEnvErr != nil {
31 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
32 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
33 | return
34 | }
35 |
36 | // Get UserId
37 | var reqData doesUserDocExistReq
38 |
39 | reqDataErr := json.NewDecoder(request.Body).Decode(&reqData)
40 | if reqDataErr != nil {
41 | http.Error(writer, reqDataErr.Error(), http.StatusInternalServerError)
42 | logger.LogErr("ReqData Decoder", reqDataErr, request)
43 | return
44 | }
45 |
46 | logger.Log("DoesUserDocExist", "Request data decoded successfully.")
47 |
48 | // Make a Firebase request to see if user document is already create with the given uid
49 | snapshot, snapshotErr := firestoreClient.Collection("users").Doc(reqData.UID).Get(context.Background())
50 | if status.Code(snapshotErr) != codes.NotFound && snapshot.Exists() {
51 | logger.Log("DoesUserDocExist", "Found snapshot for user.")
52 | doesUserDocExist = true
53 | }
54 |
55 | // Create result object
56 | result := doesUserDocExistResp{
57 | Result: doesUserDocExist,
58 | }
59 |
60 | // Send response
61 | writer.Header().Set("Content-Type", "application/json")
62 | json.NewEncoder(writer).Encode(result)
63 | }
64 |
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/doesuserdocexist
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.2.0
7 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
8 | google.golang.org/grpc v1.30.0
9 | )
10 |
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/helpers.go:
--------------------------------------------------------------------------------
1 | package doesuserdocexist
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | if os.Getenv("ENVIRONMENT") == "DEV" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
21 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
22 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
23 | }
24 |
25 | // Initialize Firestore
26 | client, err := firestore.NewClient(context.Background(), currentProject)
27 | if err != nil {
28 | return fmt.Errorf("DoesUserDocExist [Init Firestore]: %v", err)
29 | }
30 |
31 | // Initialize Sawmill
32 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "DoesUserDocExist")
33 | if err != nil {
34 | log.Printf("DoesUserDocExist [Init Sawmill]: %v", err)
35 | }
36 |
37 | firestoreClient = client
38 | logger = sawmillLogger
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/doesuserdocexist/types.go:
--------------------------------------------------------------------------------
1 | package doesuserdocexist
2 |
3 | // doesUserDocExistReq includes the uid of the user we are checking
4 | type doesUserDocExistReq struct {
5 | UID string `json:"uid"`
6 | }
7 |
8 | // doesUserDocExistResp includes a result of true or false
9 | type doesUserDocExistResp struct {
10 | Result bool `json:"result"`
11 | }
12 |
--------------------------------------------------------------------------------
/cmd/fetchallmedia/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy FetchAllMedia \
2 | --runtime go113 \
3 | --trigger-event providers/cloud.firestore/eventTypes/document.create \
4 | --trigger-resource "projects/gruvee-3b7c4/databases/(default)/documents/songs/{pushId}" \
5 | --env-vars-file ../../internal/config.yaml
--------------------------------------------------------------------------------
/cmd/fetchallmedia/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.6: Changing check for current song platforms to not be null
2 | - v1.0.0-beta.5: Reving to trigger deployment (IGNORE)
3 | - v1.0.0-beta.4: Adding better logging
4 | - v1.0.0-beta.3: Fixing merge
5 | - v1.0.0-beta.2: Reving Firebase pkg to the latest
6 | - v1.0.0-beta.1: Initializing function
--------------------------------------------------------------------------------
/cmd/fetchallmedia/fetchallmedia.go:
--------------------------------------------------------------------------------
1 | package fetchallmedia
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "strings"
8 |
9 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | var (
14 | logger sawmill.Logger
15 | )
16 |
17 | // FetchAllMedia queries all music providers for songs data when a new document is added
18 | func FetchAllMedia(ctx context.Context, event firebase.FirestoreEventSongs) error {
19 | logger.Log("FetchAllMedia", fmt.Sprintf("Event received: %v", event))
20 |
21 | initErr := initWithEnv()
22 | if initErr != nil {
23 | logger.LogErr("InitWithErr", initErr, nil)
24 | return initErr
25 | }
26 |
27 | // Init data
28 | var appleData *map[string]interface{}
29 | var spotifyData *map[string]interface{}
30 | var youtubeData *map[string]interface{}
31 |
32 | // Get media name & creator
33 | mediaName := event.Value.Fields.Name.StringValue
34 | mediaCreator := event.Value.Fields.Creator.StringValue
35 | mediaType := event.Value.Fields.Type.StringValue
36 | docPath := strings.Split(event.Value.Name, "documents/")
37 |
38 | logger.Log("FetchAllMedia", fmt.Sprintf("MediaName: %v", mediaName))
39 | logger.Log("FetchAllMedia", fmt.Sprintf("MediaCreator: %v", mediaCreator))
40 | logger.Log("FetchAllMedia", fmt.Sprintf("MediaType: %v", mediaType))
41 | logger.Log("FetchAllMedia", fmt.Sprintf("DocPath: %v", docPath))
42 |
43 | // Get media name and creator
44 | // This can be a album or playlist or song
45 | // We 1000% cannot get the playlist in another platform (until we start mapping the songs from a playlist)
46 | if mediaType == "playlist" {
47 | logger.Log("FetchAllMedia", "Media is a playlist, we don't need to run a check.")
48 | return nil
49 | }
50 |
51 | // Check each provider to see if it exists. If not, go query for that media
52 | if event.Value.Fields.Apple == (firebase.MediaMapValue{}) {
53 | // Call Apple Music Query with data
54 | logger.Log("FetchAllMedia", "Getting media for Apple Music...")
55 | }
56 |
57 | if event.Value.Fields.Spotify == (firebase.MediaMapValue{}) {
58 | // Call Spotify Query with data
59 | logger.Log("FetchAllMedia", "Getting media for Spotify...")
60 | data, queryErr := querySpotifyMedia(mediaName, mediaCreator, mediaType)
61 | if queryErr != nil {
62 | logger.LogErr("QuerySpotifyMedia", queryErr, nil)
63 | return queryErr
64 | }
65 |
66 | // Decode
67 | var spotifyQueryData spotifyQueryResp
68 | json.NewDecoder(*data).Decode(&spotifyQueryData)
69 |
70 | logger.Log("FetchAllMedia", fmt.Sprintf("SpotifyQueryData decoded: %v", spotifyQueryData))
71 |
72 | if len(spotifyQueryData.Tracks.Items) != 0 {
73 | logger.Log("FetchAllMedia", fmt.Sprintf("Found tracks: %v", spotifyQueryData.Tracks.Items[0]))
74 | // Grab first track item & create song object
75 | track := spotifyQueryData.Tracks.Items[0]
76 | spotifyData = &map[string]interface{}{
77 | "id": track.ID,
78 | "images": track.Album.Images,
79 | "url": track.ExternalURLs.Spotify,
80 | }
81 | } else if len(spotifyQueryData.Albums.Items) != 0 {
82 | logger.Log("FetchAllMedia", fmt.Sprintf("Found albums: %v", spotifyQueryData.Albums.Items[0]))
83 | // Grab first track item & create song object
84 | album := spotifyQueryData.Albums.Items[0]
85 | spotifyData = &map[string]interface{}{
86 | "id": album.ID,
87 | "images": album.Images,
88 | "url": album.ExternalURLs.Spotify,
89 | }
90 | }
91 | }
92 |
93 | if event.Value.Fields.YouTube == (firebase.MediaMapValue{}) {
94 | // Call Youtube Query with data
95 | logger.Log("FetchAllMedia", "Getting media for YouTube Music...")
96 | }
97 |
98 | // Write data to song document and check if it changed
99 | dataBlob := make(map[string]interface{})
100 |
101 | if appleData != nil {
102 | dataBlob["apple"] = appleData
103 | }
104 |
105 | if spotifyData != nil {
106 | dataBlob["spotify"] = spotifyData
107 | }
108 |
109 | if youtubeData != nil {
110 | dataBlob["youtube"] = youtubeData
111 | }
112 |
113 | if len(docPath) == 0 {
114 | error := fmt.Errorf("DocPath split was empty")
115 | logger.LogErr("DocPath Split", error, nil)
116 | return error
117 | }
118 |
119 | logger.Log("FetchAllMedia", fmt.Sprintf("Writing blob to Firestore: %v", dataBlob))
120 |
121 | // Write data to db
122 | writeDataErr := writeData(dataBlob, docPath[1])
123 | if writeDataErr != nil {
124 | logger.LogErr("WriteData", writeDataErr, nil)
125 | return writeDataErr
126 | }
127 |
128 | logger.Log("FetchAllMedia", "Data written successfully.")
129 |
130 | return nil
131 | }
132 |
--------------------------------------------------------------------------------
/cmd/fetchallmedia/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/fetchallmedia
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.3.0
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.14
8 | github.com/pixelogicdev/gruveebackend/pkg/mediahelpers v1.0.0-beta.1
9 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
10 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
11 | )
12 |
--------------------------------------------------------------------------------
/cmd/fetchallmedia/helpers.go:
--------------------------------------------------------------------------------
1 | package fetchallmedia
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 |
13 | "cloud.google.com/go/firestore"
14 | "github.com/pixelogicdev/gruveebackend/pkg/mediahelpers"
15 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
16 | "github.com/pixelogicdev/gruveebackend/pkg/social"
17 | )
18 |
19 | const (
20 | spotifyHostName = "https://api.spotify.com/v1/search"
21 | )
22 |
23 | var httpClient *http.Client
24 | var firestoreClient *firestore.Client
25 |
26 | // initWithEnv takes our yaml env variables and maps them properly.
27 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
28 | func initWithEnv() error {
29 | // Get paths
30 | var currentProject string
31 |
32 | if os.Getenv("ENVIRONMENT") == "DEV" {
33 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
34 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
35 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
36 | }
37 |
38 | // Initialize HttpClient
39 | httpClient = &http.Client{}
40 |
41 | // Initialize Firestore
42 | client, err := firestore.NewClient(context.Background(), currentProject)
43 | if err != nil {
44 | return fmt.Errorf("CreateUser [Init Firestore]: %v", err)
45 | }
46 |
47 | // Initialize Sawmill
48 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "FetchAllMedia")
49 | if err != nil {
50 | log.Printf("FetchAllMedia [Init Sawmill]: %v", err)
51 | }
52 |
53 | firestoreClient = client
54 | logger = sawmillLogger
55 | return nil
56 | }
57 |
58 | // generateRequest creats the request object to call any query API
59 | func generateRequest(uri string, method string, token string) (*http.Request, error) {
60 | // Generate request
61 | queryReq, queryReqErr := http.NewRequest(method, uri, nil)
62 | if queryReqErr != nil {
63 | return nil, fmt.Errorf("FetchAllMedia [http.NewRequest]: %v", queryReqErr)
64 | }
65 |
66 | // Add headers
67 | queryReq.Header.Add("Authorization", "Bearer "+token)
68 | return queryReq, nil
69 | }
70 |
71 | // querySpotifyMedia will use Spotify search API to pull back the data for the specified media
72 | func querySpotifyMedia(mediaName string, mediaCreator string, mediaType string) (*io.ReadCloser, error) {
73 | var mediaNameQuery string
74 |
75 | switch mediaType {
76 | case "track":
77 | mediaNameQuery = "track:" + "\"" + mediaName + "\""
78 | case "album":
79 | mediaNameQuery = "album:" + "\"" + mediaName + "\""
80 | }
81 |
82 | // Add queries
83 | query := url.Values{}
84 | query.Add("q", mediaNameQuery+"+artist:"+"\""+mediaCreator+"\"")
85 | query.Add("type", mediaType)
86 | query.Add("limit", "5")
87 |
88 | // Fetch spotify API token
89 | authToken, authTokenErr := mediahelpers.FetchSpotifyAuthToken(*firestoreClient)
90 | if authTokenErr != nil {
91 | return nil, fmt.Errorf("Spotify fetch auth token error: %v", authTokenErr)
92 | }
93 |
94 | // Setup request
95 | queryReq, queryReqErr := generateRequest(spotifyHostName, "GET", authToken.Token)
96 | if queryReqErr != nil {
97 | return nil, fmt.Errorf("Spotify generate request error: %v", queryReqErr)
98 | }
99 |
100 | // Add queries to request
101 | queryReq.URL.RawQuery = query.Encode()
102 |
103 | // Call endpoint
104 | queryResp, queryRespErr := httpClient.Do(queryReq)
105 | if queryRespErr != nil {
106 | return nil, fmt.Errorf("Spotify query error: %v", queryRespErr)
107 | }
108 |
109 | // Check if request passed back error in the body
110 | if queryResp.StatusCode != http.StatusOK {
111 | // Convert Spotify Error Object
112 | var spotifyErrorObj social.SpotifyRequestError
113 |
114 | err := json.NewDecoder(queryResp.Body).Decode(&spotifyErrorObj)
115 | if err != nil {
116 | return nil, fmt.Errorf("Spotify Request Decoder: %v", err)
117 | }
118 |
119 | return nil, fmt.Errorf("Spotify Track Request: %v", spotifyErrorObj.Error.Message)
120 | }
121 |
122 | return &queryResp.Body, nil
123 | }
124 |
125 | // writeData takes the song object data and writes it to Firestore
126 | func writeData(dataBlob map[string]interface{}, path string) error {
127 | docPath := firestoreClient.Doc(path)
128 | if docPath == nil {
129 | return fmt.Errorf("DocPath does not exist: %s", path)
130 | }
131 |
132 | _, writeErr := docPath.Set(context.Background(), dataBlob, firestore.MergeAll)
133 | if writeErr != nil {
134 | return fmt.Errorf(writeErr.Error())
135 | }
136 |
137 | return nil
138 | }
139 |
--------------------------------------------------------------------------------
/cmd/fetchallmedia/types.go:
--------------------------------------------------------------------------------
1 | package fetchallmedia
2 |
3 | import "github.com/pixelogicdev/gruveebackend/pkg/mediahelpers"
4 |
5 | // spotifyQueryResp includes the data from a search request to Spotify
6 | type spotifyQueryResp struct {
7 | Albums spotifyQueryRespAlbums `json:"albums,omitempty"`
8 | Tracks spotifyQueryRespTracks `json:"tracks,omitempty"`
9 | }
10 |
11 | // spotifyQueryRespTracks includes the list of track data
12 | type spotifyQueryRespTracks struct {
13 | Items []mediahelpers.SpotifyTrackData `json:"items"`
14 | Limit int `json:"limit"`
15 | Offset int `json:"offset"`
16 | Total int `json:"total"`
17 | }
18 |
19 | // spotifyQueryRespAlbums includes the list of album data
20 | type spotifyQueryRespAlbums struct {
21 | Items []mediahelpers.SpotifyAlbumData `json:"items"`
22 | Limit int `json:"limit"`
23 | Offset int `json:"offset"`
24 | Total int `json:"total"`
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/getapplemusicmedia/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy getAppleMusicMedia \
2 | --entry-point GetAppleMusicMedia \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/getapplemusicmedia/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.5: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.4: Adding more logging
3 | - v1.0.0-beta.3: Fixing merge
4 | - v1.0.0-beta.2: Reving Firebase package to the latest
5 | - v1.0.0-beta.1: Initializing function
--------------------------------------------------------------------------------
/cmd/getapplemusicmedia/getapplemusicmedia.go:
--------------------------------------------------------------------------------
1 | package getapplemusicmedia
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "net/http"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
11 | "github.com/pixelogicdev/gruveebackend/pkg/mediahelpers"
12 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
13 | "github.com/pixelogicdev/gruveebackend/pkg/social"
14 | )
15 |
16 | const catalogHostname = "https://api.music.apple.com/v1/catalog"
17 |
18 | var (
19 | httpClient *http.Client
20 | firestoreClient *firestore.Client
21 | logger sawmill.Logger
22 | )
23 |
24 | func init() {
25 | log.Println("GetAppleMusicMedia Initialized")
26 | }
27 |
28 | // GetAppleMusicMedia will take in Apple media data and get the exact media from Apple Music API
29 | func GetAppleMusicMedia(writer http.ResponseWriter, request *http.Request) {
30 | // Initialize
31 | initWithEnvErr := initWithEnv()
32 | if initWithEnvErr != nil {
33 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
34 | logger.LogErr("InitWithEnvErr", initWithEnvErr, nil)
35 | return
36 | }
37 |
38 | // Decode Request body to get media data
39 | var appleMusicMediaReq social.GetMediaReq
40 | appleMusicMediaReqErr := json.NewDecoder(request.Body).Decode(&appleMusicMediaReq)
41 | if appleMusicMediaReqErr != nil {
42 | http.Error(writer, appleMusicMediaReqErr.Error(), http.StatusInternalServerError)
43 | logger.LogErr("Request Decoder", appleMusicMediaReqErr, nil)
44 | return
45 | }
46 |
47 | logger.Log("GetAppleMusicMedia", "AppleMusicMediaReq decoded successfully.")
48 |
49 | // Check to see if media is already part of collection, if so, just return that
50 | mediaData, mediaDataErr := mediahelpers.GetMediaFromFirestore(*firestoreClient, appleMusicMediaReq.Provider, appleMusicMediaReq.MediaID)
51 | if mediaDataErr != nil {
52 | http.Error(writer, mediaDataErr.Error(), http.StatusInternalServerError)
53 | logger.LogErr("GetMediaFromFirestore", mediaDataErr, nil)
54 | return
55 | }
56 |
57 | // MediaData exists, return it to the client
58 | if mediaData != nil {
59 | logger.Log("GetAppleMusicMedia", "Media already exists, returning")
60 | writer.WriteHeader(http.StatusOK)
61 | writer.Header().Set("Content-Type", "application/json")
62 | json.NewEncoder(writer).Encode(mediaData)
63 | return
64 | }
65 |
66 | // MediaData does not exist, call Apple Music Endpoint
67 | // We need to get the developer token from firebase
68 | appleDevToken, appleDevTokeErr := firebase.GetAppleDeveloperToken()
69 | if appleDevTokeErr != nil {
70 | http.Error(writer, appleDevTokeErr.Error(), http.StatusInternalServerError)
71 | logger.LogErr("GetAppleDeveloperToken", appleDevTokeErr, nil)
72 | return
73 | }
74 |
75 | logger.Log("GetAppleMusicMedia", "Received Apple Developer Token.")
76 |
77 | // We only declare this here if we need to write new data
78 | var (
79 | firestoreMediaData interface{}
80 | firestoreMediaDataErr error
81 | )
82 |
83 | // Time to make our request to Apple Music API
84 | switch appleMusicMediaReq.MediaType {
85 | case "track":
86 | logger.Log("GetAppleMusicMedia", "Making track request")
87 | firestoreMediaData, firestoreMediaDataErr = getAppleMusicTrack(appleMusicMediaReq.MediaID, appleMusicMediaReq.Storefront, *appleDevToken)
88 | if firestoreMediaDataErr != nil {
89 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
90 | logger.LogErr("GetAppleMusicTrack", firestoreMediaDataErr, nil)
91 | return
92 | }
93 | case "playlist":
94 | logger.Log("GetAppleMusicMedia", "Making playlist request")
95 | firestoreMediaData, firestoreMediaDataErr = getAppleMusicPlaylist(appleMusicMediaReq.MediaID, appleMusicMediaReq.Storefront, *appleDevToken)
96 | if firestoreMediaDataErr != nil {
97 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
98 | logger.LogErr("GetAppleMusicPlaylist", firestoreMediaDataErr, nil)
99 | return
100 | }
101 | case "album":
102 | logger.Log("GetAppleMusicMedia", "Making album request")
103 | firestoreMediaData, firestoreMediaDataErr = getAppleMusicAlbum(appleMusicMediaReq.MediaID, appleMusicMediaReq.Storefront, *appleDevToken)
104 | if firestoreMediaDataErr != nil {
105 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
106 | logger.LogErr("GetAppleMusicAlbum", firestoreMediaDataErr, nil)
107 | return
108 | }
109 | default:
110 | http.Error(writer, appleMusicMediaReq.MediaType+" media type does not exist", http.StatusInternalServerError)
111 | logger.LogErr("GetAppleMusicDefault", fmt.Errorf("%v media type does not exist", appleMusicMediaReq.MediaType), nil)
112 | return
113 | }
114 |
115 | logger.Log("GetAppleMusicMedia", "Successfully got Apple Music Media.")
116 |
117 | writer.WriteHeader(http.StatusOK)
118 | writer.Header().Set("Content-Type", "application/json")
119 | json.NewEncoder(writer).Encode(firestoreMediaData)
120 | }
121 |
--------------------------------------------------------------------------------
/cmd/getapplemusicmedia/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/getapplemusicmedia
2 |
3 | go 1.13
4 |
--------------------------------------------------------------------------------
/cmd/getapplemusicmedia/types.go:
--------------------------------------------------------------------------------
1 | package getapplemusicmedia
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // -- RESPONSES -- //
8 |
9 | // appleMusicTrackResp defines the data returned and needed from the Apple Music Get Track API
10 | type appleMusicTrackResp struct {
11 | Data []appleMusicTrackData `json:"data"`
12 | }
13 |
14 | // appleMusicPlaylistResp defines the data returned and needed from the Apple Music Get Playlist API
15 | type appleMusicPlaylistResp struct {
16 | Data []appleMusicPlaylistData `json:"data"`
17 | }
18 |
19 | // appleMusicAlbumResp defines the data retuned and needed from the Apple Music Get Album API
20 | type appleMusicAlbumResp struct {
21 | Data []appleMusicAlbumData `json:"data"`
22 | }
23 |
24 | // -- DATA -- //
25 |
26 | // appleMusicTrackData defines the track data inside the data array response
27 | type appleMusicTrackData struct {
28 | Attributes struct {
29 | AlbumName string `json:"albumName"`
30 | ArtistName string `json:"artistName"`
31 | Artwork appleMusicArtwork `json:"artwork"`
32 | TrackName string `json:"name"`
33 | ExternalURL string `json:"url"`
34 | } `json:"attributes"`
35 | }
36 |
37 | // appleMusicPlaylistData defines the playlist data inside the data array response
38 | type appleMusicPlaylistData struct {
39 | Attributes struct {
40 | Artwork appleMusicArtwork `json:"artwork"`
41 | CuratorName string `json:"curatorName"`
42 | Description appleMusicEditorialNotes `json:"description"`
43 | LastModifiedDate time.Time `json:"lastModifiedDate"`
44 | Name string `json:"name"`
45 | PlaylistType appleMusicPlaylistType `json:"playlistType"`
46 | URL string `json:"url"`
47 | } `json:"attributes"`
48 | Relationships struct {
49 | Tracks appleMusicTrackRelationship `json:"tracks"`
50 | } `json:"relationships"`
51 | Type string `json:"type"`
52 | }
53 |
54 | // appleMusicAlbumData defines the album data inside the data array response
55 | type appleMusicAlbumData struct {
56 | Attributes struct {
57 | AlbumName string `json:"albumName"`
58 | ArtistName string `json:"artistName"`
59 | Artwork appleMusicArtwork `json:"artwork"`
60 | ContentRating string `json:"contentRating"`
61 | Copyright string `json:"copyright"`
62 | EditorialNotes appleMusicEditorialNotes `json:"editorialNotes"`
63 | GenreNames []string `json:"genreNames"`
64 | IsComplete bool `json:"isComplete"`
65 | IsSingle bool `json:"isSingle"`
66 | Name string `json:"name"`
67 | RecordLabel string `json:"recordLabel"`
68 | ReleaseDate string `json:"releaseDate"`
69 | TrackCount int `json:"trackCount"`
70 | URL string `json:"url"`
71 | IsMasteredForItunes bool `json:"isMasteredForItunes"`
72 | } `json:"attributes"`
73 |
74 | Relationships struct {
75 | Tracks appleMusicTrackRelationship `json:"tracks"`
76 | } `json:"relationships"`
77 | }
78 |
79 | // appleMusicVideoData defines the music video data
80 | type appleMusicVideoData struct {
81 | Attributes struct {
82 | AlbumName string `json:"albumName"`
83 | ArtistName string `json:"artistName"`
84 | Artwork appleMusicArtwork `json:"artwork"`
85 | ContentRating string `json:"contentRating"`
86 | DurationInMS float64 `json:"durationInMillis"`
87 | EditorialNotes appleMusicEditorialNotes `json:"editorialNotes"`
88 | GenreNames []string `json:"genreNames"`
89 | ISRC string `json:"isrc"`
90 | Name string `json:"name"`
91 | ReleaseDate time.Time `json:"releaseDate"`
92 | TrackNumber int `json:"trackNumber"`
93 | URL string `json:"url"`
94 | VideoSubType string `json:"videoSubType"`
95 | HasHDR bool `json:"hadHDR"`
96 | Has4k bool `json:"has4k"`
97 | } `json:"attributes"`
98 | }
99 |
100 | // -- DATA TYPES -- //
101 |
102 | // appleMusicPlaylistType defines the different possible playlist types
103 | type appleMusicPlaylistType string
104 |
105 | // appleMusicArtwork defines the artwork properties of Apple Music media
106 | type appleMusicArtwork struct {
107 | BGColor string `json:"bgColor" firestore:"bgColor"`
108 | Height int `json:"height" firestore:"height"`
109 | TextColor1 string `json:"textColor1,omitempty" firestore:"textColor1,omitempty"`
110 | TextColor2 string `json:"textColor2,omitempty" firestore:"textColor2,omitempty"`
111 | TextColor3 string `json:"textColor3,omitempty" firestore:"textColor3,omitempty"`
112 | TextColor4 string `json:"textColor4,omitempty" firestore:"textColor4,omitempty"`
113 | URL string `json:"url" firestore:"url"`
114 | Width int `json:"width" firestore:"width"`
115 | }
116 |
117 | // appleMusicEditorialNotes defines the description of a playlist
118 | type appleMusicEditorialNotes struct {
119 | Short string `json:"short"`
120 | Standard string `json:"standard"`
121 | }
122 |
123 | // appleMusicTrackRelationship defines the data the content of a playlist
124 | // Data here can be appleMusicTrackData or appleMusicVideoData
125 | type appleMusicTrackRelationship struct {
126 | Data []interface{} `json:"data"`
127 | }
128 |
129 | // -- CONSTANTS -- //
130 | // The different options possible for a PlaylistType
131 | const (
132 | userShared appleMusicPlaylistType = "user-shared"
133 | editorial appleMusicPlaylistType = "editorial"
134 | external appleMusicPlaylistType = "external"
135 | personalMix appleMusicPlaylistType = "personal-mix"
136 | )
137 |
--------------------------------------------------------------------------------
/cmd/getspotifymedia/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy getSpotifyMedia \
2 | --entry-point GetSpotifyMedia \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/getspotifymedia/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.7: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.6: Adding more logging
3 | - v1.0.0-beta.5: Fixing merge
4 | - v1.0.0-beta.4: Reving Firebase package to the latest
5 | - v1.0.0-beta.3: Moving shared functions to helpers file
6 | - v1.0.0-beta.2: Changing enviornment variable check location
7 | - v1.0.0-beta.1: Initializing createspotifymedia
--------------------------------------------------------------------------------
/cmd/getspotifymedia/getspotifymedia.go:
--------------------------------------------------------------------------------
1 | package getspotifymedia
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "net/http"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/mediahelpers"
11 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
12 | "github.com/pixelogicdev/gruveebackend/pkg/social"
13 | )
14 |
15 | const (
16 | spotifyAccessTokenURI = "https://accounts.spotify.com/api/token"
17 | spotifyGetTrackURI = "https://api.spotify.com/v1/tracks"
18 | spotifyGetPlaylistURI = "https://api.spotify.com/v1/playlists"
19 | spotifyGetAlbumURI = "https://api.spotify.com/v1/albums"
20 | )
21 |
22 | var (
23 | httpClient *http.Client
24 | firestoreClient *firestore.Client
25 | logger sawmill.Logger
26 | )
27 |
28 | // Draco401 - "Draco401 was here." (04/17/20)
29 | func init() {
30 | log.Println("GetSpotifyMedia Initialized")
31 | }
32 |
33 | // GetSpotifyMedia will take in Spotify media data and get the exact media from Spotify API
34 | func GetSpotifyMedia(writer http.ResponseWriter, request *http.Request) {
35 | // Initialize
36 | initWithEnvErr := initWithEnv()
37 | if initWithEnvErr != nil {
38 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
39 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
40 | return
41 | }
42 |
43 | // Decode Request body to get track data
44 | var spotifyMediaReq social.GetMediaReq
45 | spotifyReqDecodeErr := json.NewDecoder(request.Body).Decode(&spotifyMediaReq)
46 | if spotifyReqDecodeErr != nil {
47 | http.Error(writer, spotifyReqDecodeErr.Error(), http.StatusInternalServerError)
48 | logger.LogErr("Request Decoder", spotifyReqDecodeErr, request)
49 | return
50 | }
51 |
52 | logger.Log("GetSpotifyMedia", "Decoded response.")
53 |
54 | // Check to see if media is already part of collection, if so, just return that
55 | mediaData, mediaDataErr := mediahelpers.GetMediaFromFirestore(*firestoreClient, spotifyMediaReq.Provider, spotifyMediaReq.MediaID)
56 | if mediaDataErr != nil {
57 | http.Error(writer, mediaDataErr.Error(), http.StatusInternalServerError)
58 | logger.LogErr("GetMediaFromFirestore", mediaDataErr, request)
59 | return
60 | }
61 |
62 | logger.Log("GetSpotifyMedia", "Recieved media from Firestore")
63 |
64 | // MediaData exists, return it to the client
65 | if mediaData != nil {
66 | logger.Log("GetSpotifyMedia", "Media already exists, returning.")
67 | writer.WriteHeader(http.StatusOK)
68 | writer.Header().Set("Content-Type", "application/json")
69 | json.NewEncoder(writer).Encode(mediaData)
70 | return
71 | }
72 |
73 | // Get Spotify access token (currently getting access token of user)
74 | creds, credErr := getCreds()
75 | if credErr != nil {
76 | http.Error(writer, credErr.Error(), http.StatusInternalServerError)
77 | logger.LogErr("GetCreds", credErr, nil)
78 | return
79 | }
80 |
81 | logger.Log("GetSpotifyMedia", "Receieved credentials")
82 |
83 | // We only initialize this if we need to call Spotify search
84 | var (
85 | firestoreMediaData interface{}
86 | firestoreMediaDataErr error
87 | )
88 |
89 | // Setup and call Spotify search
90 | switch spotifyMediaReq.MediaType {
91 | case "track":
92 | logger.Log("GetSpotifyMedia", "Calling track API")
93 | firestoreMediaData, firestoreMediaDataErr = getSpotifyTrack(spotifyMediaReq.MediaID, creds.Token)
94 | if firestoreMediaDataErr != nil {
95 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
96 | logger.LogErr("GetSpotifyAlbum Switch", firestoreMediaDataErr, request)
97 | return
98 | }
99 | case "playlist":
100 | logger.Log("GetSpotifyMedia", "Calling playlist API")
101 | firestoreMediaData, firestoreMediaDataErr = getSpotifyPlaylist(spotifyMediaReq.MediaID, creds.Token)
102 | if firestoreMediaDataErr != nil {
103 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
104 | logger.LogErr("GetSpotifyAlbum Switch", firestoreMediaDataErr, request)
105 | return
106 | }
107 | case "album":
108 | logger.Log("GetSpotifyMedia", "Calling album API")
109 | firestoreMediaData, firestoreMediaDataErr = getSpotifyAlbum(spotifyMediaReq.MediaID, creds.Token)
110 | if firestoreMediaDataErr != nil {
111 | http.Error(writer, firestoreMediaDataErr.Error(), http.StatusInternalServerError)
112 | logger.LogErr("GetSpotifyAlbum Switch", firestoreMediaDataErr, request)
113 | return
114 | }
115 | default:
116 | http.Error(writer, spotifyMediaReq.MediaType+" media type does not exist", http.StatusInternalServerError)
117 | logger.LogErr("MediaTypeSwitch", fmt.Errorf("%v media type does not exist", spotifyMediaReq.MediaType), request)
118 | return
119 | }
120 |
121 | logger.Log("GetSpotifyMedia", "Successfully received media data.")
122 |
123 | writer.WriteHeader(http.StatusOK)
124 | writer.Header().Set("Content-Type", "application/json")
125 | json.NewEncoder(writer).Encode(firestoreMediaData)
126 | }
127 |
--------------------------------------------------------------------------------
/cmd/getspotifymedia/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/getspotifymedia
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | google.golang.org/grpc v1.28.0
11 | )
12 |
--------------------------------------------------------------------------------
/cmd/getspotifymedia/types.go:
--------------------------------------------------------------------------------
1 | package getspotifymedia
2 |
3 | import "github.com/pixelogicdev/gruveebackend/pkg/firebase"
4 |
5 | // getSpotifyMediaReq takes in the data needed to request the media data from Spotify
6 | type getSpotifyMediaReq struct {
7 | Provider string `json:"provider"`
8 | MediaID string `json:"mediaId"`
9 | MediaType string `json:"mediaType"`
10 | }
11 |
12 | // spotifyTrackResp defines the data returned and needed from the Spotify Get Track API
13 | type spotifyTrackResp struct {
14 | ID string `json:"id"`
15 | Name string `json:"name"`
16 | Artists []spotifyArtist `json:"artists"`
17 | Type string `json:"type"`
18 | Album spotifyAlbum `json:"album"`
19 | ExternalURLs struct {
20 | Spotify string `json:"spotify"`
21 | } `json:"external_urls"`
22 | }
23 |
24 | // spotifyPlaylistResp defines the data returned and needed from the Spotify Get Playlist API
25 | type spotifyPlaylistResp struct {
26 | ID string `json:"id"`
27 | Name string `json:"name"`
28 | Type string `json:"type"`
29 | Images []firebase.SpotifyImage `json:"images"`
30 | Owner struct {
31 | DisplayName string `json:"display_name"`
32 | } `json:"owner"`
33 | ExternalURLs struct {
34 | Spotify string `json:"spotify"`
35 | } `json:"external_urls"`
36 | }
37 |
38 | // spotifyAlbum defines the data returned and needed from the Spotify Get Track API
39 | type spotifyAlbum struct {
40 | ID string `json:"id"`
41 | Name string `json:"name"`
42 | Type string `json:"type"`
43 | Artists []spotifyArtist `json:"artists"`
44 | Images []firebase.SpotifyImage `json:"images"`
45 | ExternalURLs struct {
46 | Spotify string `json:"spotify"`
47 | } `json:"external_urls"`
48 | }
49 |
50 | // spotifyArtist defines the data returned and needed from the Spotify Get Track API
51 | type spotifyArtist struct {
52 | ID string `json:"id"`
53 | Name string `json:"name"`
54 | Type string `json:"type"`
55 | ExternalURLs struct {
56 | Spotify string `json:"spotify"`
57 | } `json:"external_urls"`
58 | }
59 |
--------------------------------------------------------------------------------
/cmd/socialplatform/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy createSocialPlatform \
2 | --entry-point CreateSocialPlatform \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/socialplatform/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.6: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.5: Adding more logging & creating helpers file
3 | - v1.0.0-beta.4: Fixing merge
4 | - v1.0.0-beta.3: Reving firebase package to latest
5 | - v1.0.0-beta.2: Changing enviornment variable check location
6 | - v1.0.0-beta.1: Reving firebase package to latest
--------------------------------------------------------------------------------
/cmd/socialplatform/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/socialplatform
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | )
10 |
--------------------------------------------------------------------------------
/cmd/socialplatform/helpers.go:
--------------------------------------------------------------------------------
1 | package socialplatform
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | if os.Getenv("ENVIRONMENT") == "DEV" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
21 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
22 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
23 | }
24 |
25 | // Initialize Firestore
26 | client, err := firestore.NewClient(context.Background(), currentProject)
27 | if err != nil {
28 | return fmt.Errorf("SocialTokenRefresh [Init Firestore]: %v", err)
29 | }
30 |
31 | // Initialize Sawmill
32 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "CreateSocialPlatform")
33 | if err != nil {
34 | log.Printf("CreateSocialPlatform [Init Sawmill]: %v", err)
35 | }
36 |
37 | firestoreClient = client
38 | logger = sawmillLogger
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/socialplatform/socialplatform.go:
--------------------------------------------------------------------------------
1 | package socialplatform
2 |
3 | // eminyilmazz - "If I got corona, this line is my legacy." (03/12/20)
4 | import (
5 | "context"
6 | "encoding/json"
7 | "log"
8 | "net/http"
9 |
10 | "cloud.google.com/go/firestore"
11 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
12 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
13 | )
14 |
15 | var (
16 | firestoreClient *firestore.Client
17 | logger sawmill.Logger
18 | )
19 |
20 | // JackGamesFTW - "TriHard 7" (03/18/20)
21 | func init() {
22 | log.Println("CreateSocialPlatform intialized")
23 | }
24 |
25 | // CreateSocialPlatform will write a new social platform to firestore
26 | func CreateSocialPlatform(writer http.ResponseWriter, request *http.Request) {
27 | // Initialize
28 | initWithEnvErr := initWithEnv()
29 | if initWithEnvErr != nil {
30 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
31 | logger.LogErr("InitWithEnvErr", initWithEnvErr, nil)
32 | return
33 | }
34 |
35 | var socialPlatform firebase.FirestoreSocialPlatform
36 |
37 | socialPlatformErr := json.NewDecoder(request.Body).Decode(&socialPlatform)
38 | if socialPlatformErr != nil {
39 | http.Error(writer, socialPlatformErr.Error(), http.StatusInternalServerError)
40 | logger.LogErr("SocialPlatform Decoder", socialPlatformErr, request)
41 | return
42 | }
43 |
44 | logger.Log("CreateSocialPlatform", "Decoded socialPlatform.")
45 |
46 | // Write SocialPlatform to Firestore
47 | _, writeErr := firestoreClient.Collection("social_platforms").Doc(socialPlatform.ID).Set(context.Background(), socialPlatform)
48 | if writeErr != nil {
49 | http.Error(writer, writeErr.Error(), http.StatusInternalServerError)
50 | logger.LogErr("FireStore Set", writeErr, nil)
51 | return
52 | }
53 |
54 | logger.Log("CreateSocialPlatform", "Successfully wrote platform to firestore.")
55 |
56 | writer.WriteHeader(http.StatusOK)
57 | }
58 |
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy socialTokenRefresh \
2 | --entry-point SocialTokenRefresh \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.8: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.7: Adding more logging & adding types and helpers files
3 | - v1.0.0-beta.6: Fixing merge
4 | - v1.0.0-beta.5: Reving firebase and social package to latest
5 | - v1.0.0-beta.4: Changing enviornment variable check location
6 | - v1.0.0-beta.3: Tweaking return props & reving social pkg
7 | - v1.0.0-beta.2: Reving firebase and social package to latest
8 | - v1.0.0-beta.1: Reving firebase package to latest
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/socialtokenrefresh
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | )
11 |
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/helpers.go:
--------------------------------------------------------------------------------
1 | package socialtokenrefresh
2 |
3 | import (
4 | "context"
5 | "encoding/base64"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | "strings"
13 | "time"
14 |
15 | "cloud.google.com/go/firestore"
16 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
17 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
18 | "github.com/pixelogicdev/gruveebackend/pkg/social"
19 | )
20 |
21 | // initWithEnv takes our yaml env variables and maps them properly.
22 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
23 | func initWithEnv() error {
24 | // Get paths
25 | var currentProject string
26 |
27 | if os.Getenv("ENVIRONMENT") == "DEV" {
28 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
29 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
30 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
31 | }
32 |
33 | // Set httpClient
34 | httpClient = &http.Client{}
35 |
36 | // Initialize Firestore
37 | client, err := firestore.NewClient(context.Background(), currentProject)
38 | if err != nil {
39 | return fmt.Errorf("SocialTokenRefresh [Init Firestore]: %v", err)
40 | }
41 |
42 | // Initialize Sawmill
43 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "SocialTokenRefersh")
44 | if err != nil {
45 | log.Printf("SocialTokenRefresh [Init Sawmill]: %v", err)
46 | }
47 |
48 | firestoreClient = client
49 | logger = sawmillLogger
50 | return nil
51 | }
52 |
53 | func getUserPlatformsToRefresh(uid string) (*[]firebase.FirestoreSocialPlatform, error) {
54 | logger.Log("GetUserPlatformsToRefresh", "Starting...")
55 |
56 | // Go to Firebase and get document references for all social platforms
57 | snapshot, snapshotErr := firestoreClient.Collection("users").Doc(uid).Get(context.Background())
58 | if snapshotErr != nil {
59 | return nil, fmt.Errorf(snapshotErr.Error())
60 | }
61 |
62 | logger.Log("GetUserPlatformsToRefresh", "Received snapshot.")
63 |
64 | // Grab socialPlatforms array
65 | var firestoreUser firebase.FirestoreUser
66 | dataToErr := snapshot.DataTo(&firestoreUser)
67 | if dataToErr != nil {
68 | return nil, fmt.Errorf(dataToErr.Error())
69 | }
70 |
71 | logger.Log("GetUserPlatformsToRefresh", "Received snapshot data.")
72 |
73 | // WE SHOULD BE: checking to see which platforms need to be refreshed
74 | // Currently we are not, just returning all platforms
75 | socialPlatforms, fetchRefErr := fetchChildRefs(firestoreUser.SocialPlatforms)
76 | if fetchRefErr != nil {
77 | return nil, fmt.Errorf(fetchRefErr.Error())
78 | }
79 |
80 | logger.Log("GetUserPlatformsToRefresh", "Successfully refreshed tokens.")
81 |
82 | // Return those platforms to main
83 | return socialPlatforms, nil
84 | }
85 |
86 | // fetchChildRefs will convert document references to FiresstoreSocilaPlatform Objects
87 | func fetchChildRefs(refs []*firestore.DocumentRef) (*[]firebase.FirestoreSocialPlatform, error) {
88 | logger.Log("FetchChildRefs", "Starting...")
89 |
90 | docsnaps, err := firestoreClient.GetAll(context.Background(), refs)
91 | if err != nil {
92 | return nil, fmt.Errorf("fetchChildRefs: %v", err)
93 | }
94 |
95 | logger.Log("FetchChildRefs", "Received all child documents.")
96 |
97 | var socialPlatforms []firebase.FirestoreSocialPlatform
98 | for _, userSnap := range docsnaps {
99 | var socialPlatform firebase.FirestoreSocialPlatform
100 |
101 | dataErr := userSnap.DataTo(&socialPlatform)
102 | if dataErr != nil {
103 | logger.Log("FetchChildRefs", "Encountered error while parsing userSnapshot.")
104 | logger.LogErr("FetchChildRefs", dataErr, nil)
105 | continue
106 | }
107 |
108 | socialPlatforms = append(socialPlatforms, socialPlatform)
109 | }
110 |
111 | logger.Log("FetchChildRefs", "Successfully received social platforms.")
112 |
113 | return &socialPlatforms, nil
114 | }
115 |
116 | // refreshTokens goes through social platform objects and refreshes tokens as necessary
117 | func refreshTokens(socialPlatforms []firebase.FirestoreSocialPlatform) social.RefreshTokensResponse {
118 | logger.Log("RefreshTokens", "Starting...")
119 |
120 | // Get current time
121 | var currentTime = time.Now()
122 | var refreshTokensResp = social.RefreshTokensResponse{
123 | RefreshTokens: map[string]firebase.APIToken{},
124 | }
125 |
126 | for _, platform := range socialPlatforms {
127 | logger.Log("RefreshTokens", fmt.Sprintf("Expires In: %d seconds\n", platform.APIToken.ExpiresIn))
128 | logger.Log("RefreshTokens", fmt.Sprintf("Expired At: %s\n", platform.APIToken.ExpiredAt))
129 | logger.Log("RefreshTokens", fmt.Sprintf("Created At: %s\n", platform.APIToken.CreatedAt))
130 |
131 | expiredAtTime, expiredAtTimeErr := time.Parse(time.RFC3339, platform.APIToken.ExpiredAt)
132 | if expiredAtTimeErr != nil {
133 | fmt.Println(expiredAtTimeErr.Error())
134 | continue
135 | }
136 |
137 | logger.Log("RefreshTokens", "Successfully parsed expiredAtTime")
138 |
139 | if currentTime.After(expiredAtTime) {
140 | logger.Log("RefreshTokens", fmt.Sprintf("%s access token is expired. Calling Refresh...\n", platform.PlatformName))
141 |
142 | // Call API refresh
143 | refreshToken, tokenActionErr := refreshTokenAction(platform)
144 | if tokenActionErr != nil {
145 | logger.LogErr("RefreshToken", tokenActionErr, nil)
146 | continue
147 | }
148 |
149 | var expiredAtStr = time.Now().Add(time.Second * time.Duration(refreshToken.ExpiresIn))
150 | var refreshedAPIToken = firebase.APIToken{
151 | CreatedAt: time.Now().Format(time.RFC3339),
152 | ExpiredAt: expiredAtStr.Format(time.RFC3339),
153 | ExpiresIn: refreshToken.ExpiresIn,
154 | Token: refreshToken.AccessToken,
155 | }
156 |
157 | // Set refresh token in map
158 | refreshTokensResp.RefreshTokens[platform.PlatformName] = refreshedAPIToken
159 |
160 | // Set new token data in database
161 | writeTokenErr := writeToken(platform.ID, refreshedAPIToken)
162 | if writeTokenErr != nil {
163 | logger.LogErr("RefreshToken", writeTokenErr, nil)
164 | continue
165 | }
166 | }
167 | }
168 |
169 | logger.Log("RefreshToken", "Successfully refreshed all platforms.")
170 |
171 | return refreshTokensResp
172 | }
173 |
174 | // refreshTokenAction will call the API per platform and return the data needed
175 | func refreshTokenAction(platform firebase.FirestoreSocialPlatform) (*spotifyRefreshTokenRes, error) {
176 | logger.Log("RefreshTokenAction", "Starting...")
177 |
178 | var authStr = os.Getenv("SPOTIFY_CLIENTID") + ":" + os.Getenv("SPOTIFY_SECRET")
179 |
180 | // Create Request
181 | data := url.Values{}
182 | data.Set("grant_type", "refresh_token")
183 | data.Set("refresh_token", platform.RefreshToken)
184 |
185 | refreshTokenReq, refreshTokenReqErr := http.NewRequest("POST", spotifyRefreshTokenURI,
186 | strings.NewReader(data.Encode()))
187 | if refreshTokenReqErr != nil {
188 | return nil, fmt.Errorf(refreshTokenReqErr.Error())
189 | }
190 |
191 | logger.Log("RefreshTokenAction", "Generated Request.")
192 |
193 | refreshTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
194 | refreshTokenReq.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(authStr)))
195 | customTokenResp, httpErr := httpClient.Do(refreshTokenReq)
196 | if httpErr != nil {
197 | return nil, fmt.Errorf(httpErr.Error())
198 | }
199 |
200 | logger.Log("RefreshTokenAction", "Received CustomToken Response.")
201 |
202 | // Decode the token to send back
203 | var spotifyRefreshRes spotifyRefreshTokenRes
204 | refreshTokenDecodeErr := json.NewDecoder(customTokenResp.Body).Decode(&spotifyRefreshRes)
205 | if refreshTokenDecodeErr != nil {
206 | return nil, fmt.Errorf(refreshTokenDecodeErr.Error())
207 | }
208 |
209 | logger.Log("RefreshTokenAction", "Successfully decoded response.")
210 |
211 | // Make sure to add platform name here before continuing
212 | spotifyRefreshRes.PlatformName = platform.PlatformName
213 | return &spotifyRefreshRes, nil
214 | }
215 |
216 | // writeToken will write the new APIToken object to the social platform document
217 | func writeToken(platformID string, token firebase.APIToken) error {
218 | logger.Log("WriteToken", "Starting...")
219 |
220 | // Write new APIToken, ExpiredAt, ExpiresIn, CreatedAt
221 | platformDoc := firestoreClient.Collection("social_platforms").Doc(platformID)
222 | if platformDoc == nil {
223 | return fmt.Errorf("platformId %s could not be found", platformID)
224 | }
225 |
226 | logger.Log("WriteToken", "Received document.")
227 |
228 | // Time to update
229 | _, writeErr := platformDoc.Update(context.Background(), []firestore.Update{{Path: "apiToken", Value: token}})
230 | if writeErr != nil {
231 | return fmt.Errorf(writeErr.Error())
232 | }
233 |
234 | logger.Log("WriteToken", "Successfully wrote document.")
235 |
236 | return nil
237 | }
238 |
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/socialtokenrefresh.go:
--------------------------------------------------------------------------------
1 | package socialtokenrefresh
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "net/http"
8 | "os"
9 |
10 | "cloud.google.com/go/firestore"
11 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
12 | "github.com/pixelogicdev/gruveebackend/pkg/social"
13 | )
14 |
15 | const spotifyRefreshTokenURI = "https://accounts.spotify.com/api/token"
16 |
17 | var (
18 | httpClient *http.Client
19 | firestoreClient *firestore.Client
20 | logger sawmill.Logger
21 | )
22 |
23 | func init() {
24 | log.Println("SocialTokenRefresh initialized")
25 | }
26 |
27 | // SocialTokenRefresh checks to see if we need to refresh current API tokens for social platforms
28 | func SocialTokenRefresh(writer http.ResponseWriter, request *http.Request) {
29 | // Check to see if we have env variables
30 | if os.Getenv("SPOTIFY_CLIENTID") == "" || os.Getenv("SPOTIFY_SECRET") == "" {
31 | logger.LogErr("SocialTokenRefresh", fmt.Errorf("Spotify ClientID not found in environment env"), nil)
32 | return
33 | }
34 |
35 | // Initialize
36 | initWithEnvErr := initWithEnv()
37 | if initWithEnvErr != nil {
38 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
39 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
40 | return
41 | }
42 |
43 | // Receive payload that includes uid
44 | var socialTokenReq social.TokenRefreshRequest
45 |
46 | // Decode payload
47 | socialTokenErr := json.NewDecoder(request.Body).Decode(&socialTokenReq)
48 | if socialTokenErr != nil {
49 | http.Error(writer, socialTokenErr.Error(), http.StatusInternalServerError)
50 | logger.LogErr("SocialTokenReq Decoder", socialTokenErr, request)
51 | return
52 | }
53 |
54 | logger.Log("SocialTokenRefresh", "Decoded request")
55 |
56 | // Go to Firestore and get the platforms for user
57 | platsToRefresh, platformErr := getUserPlatformsToRefresh(socialTokenReq.UID)
58 | if platformErr != nil {
59 | http.Error(writer, platformErr.Error(), http.StatusInternalServerError)
60 | logger.LogErr("GetUserPlatforms", platformErr, request)
61 | return
62 | }
63 |
64 | logger.Log("SocialTokenRefresh", "Received SocialPlatform.")
65 |
66 | if platsToRefresh != nil && len(*platsToRefresh) == 0 {
67 | logger.Log("SocialTokenRefresh", "No social platforms need to be refreshed.")
68 |
69 | // No refresh needed, lets return this with no content
70 | writer.WriteHeader(http.StatusNoContent)
71 | return
72 | }
73 |
74 | // Run refresh token logic
75 | refreshTokenResp := refreshTokens(*platsToRefresh)
76 |
77 | logger.Log("SocialTokenRefresh", "Tokens successfully refreshed.")
78 |
79 | writer.WriteHeader(http.StatusOK)
80 | writer.Header().Set("Content-Type", "application/json")
81 | json.NewEncoder(writer).Encode(refreshTokenResp)
82 | }
83 |
--------------------------------------------------------------------------------
/cmd/socialtokenrefresh/types.go:
--------------------------------------------------------------------------------
1 | package socialtokenrefresh
2 |
3 | // spotifyRefreshTokenRes contains the response from Spotify when trying to refresh the access token
4 | type spotifyRefreshTokenRes struct {
5 | PlatformName string `json:"platformName"`
6 | AccessToken string `json:"access_token"`
7 | TokenType string `json:"token_type"`
8 | Scope string `json:"scope"`
9 | ExpiresIn int `json:"expires_in"`
10 | }
11 |
--------------------------------------------------------------------------------
/cmd/spotifyauth/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy authorizeWithSpotify \
2 | --entry-point AuthorizeWithSpotify \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/spotifyauth/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.10: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.9: Adding more logging & adding types and helpers files.
3 | - v1.0.0-beta.8: Adding more log statements for test GH Action deployment
4 | - v1.0.0-beta.7: Fixing merge
5 | - v1.0.0-beta.6: Reving firebase package to latest
6 | - v1.0.0-beta.5: Changing enviornment variable check location
7 | - v1.0.0-beta.4: Adding proper generatetoken endpoint
8 | - v1.0.0-beta.3: Reving firebase and social package to latest
9 | - v1.0.0-beta.2: Reving firebase package & adding support for Playlist Type
10 | - v1.0.0-beta.1: Reving firebase package to latest
--------------------------------------------------------------------------------
/cmd/spotifyauth/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/spotifyauth
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.1.1
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | google.golang.org/grpc v1.28.0
11 | )
12 |
--------------------------------------------------------------------------------
/cmd/spotifyauth/spotifyauth.go:
--------------------------------------------------------------------------------
1 | package spotifyauth
2 |
3 | // InukApp - "Todo: add Plex auth support" (03/22/20)
4 | // DaedTTV - "32 Font Size Kinda THICC" (03/23/20)
5 | // thoastyk "X O X" (02/26/20)
6 | // thoastyk "_ X O" (02/26/20)
7 | // pheonix_d123 "O O X I wanna interrupt the tic-tac-toe." (03/08/20)
8 | // Belonix97 "X O O I want to interrupt the interrupted tic-tac-toe line." (03/08/20)
9 | // ItsAstrix "O O X I wanna interrupt the tic-tac-toe." (03/08/20)
10 | // thoastyk "X _ O" (02/26/20)
11 | // creativenobu - "Have you flutter tried?" (02/26/20)
12 | // TheDkbay - "If this were made in Flutter Alec would already be done but he loves to pain himself and us by using inferior technology maybe he will learn in the future." (03/02/20)
13 | // OnePocketPimp - "Alec had an Idea at this moment in time 9:53 am 3-1-2020" (03/01/20)
14 | // ZenonLoL - "go mod vendor - it just works" (03/08/20)
15 | // gamma7869 - "Maybe if I get Corona, I could finally get friends. Corona Friends?" (03/12/20)
16 | import (
17 | "encoding/json"
18 | "fmt"
19 | "log"
20 | "net/http"
21 |
22 | "cloud.google.com/go/firestore"
23 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
24 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
25 | "github.com/pixelogicdev/gruveebackend/pkg/social"
26 | )
27 |
28 | const spotifyMeURI = "https://api.spotify.com/v1/me"
29 |
30 | var (
31 | httpClient *http.Client
32 | firestoreClient *firestore.Client
33 | logger sawmill.Logger
34 | hostname string
35 | )
36 |
37 | func init() {
38 | log.Println("AuthorizeWithSpotify initialized")
39 | }
40 |
41 | // AuthorizeWithSpotify will verify Spotify creds are valid and return any associated Firebase user or create a new Firebase user
42 | func AuthorizeWithSpotify(writer http.ResponseWriter, request *http.Request) {
43 | // Initialize
44 | initWithEnvErr := initWithEnv()
45 | if initWithEnvErr != nil {
46 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
47 | logger.LogErr("InitWithEnvErr", initWithEnvErr, nil)
48 | return
49 | }
50 |
51 | var spotifyAuthRequest social.SpotifyAuthRequest
52 |
53 | authResponseErr := json.NewDecoder(request.Body).Decode(&spotifyAuthRequest)
54 | if authResponseErr != nil {
55 | http.Error(writer, authResponseErr.Error(), http.StatusInternalServerError)
56 | logger.LogErr("SpotifyAuthRequest Decoder", authResponseErr, request)
57 | return
58 | }
59 |
60 | log.Printf("Decoded SpotifyAuthRequest: %v", spotifyAuthRequest)
61 |
62 | if len(spotifyAuthRequest.APIToken) == 0 {
63 | http.Error(writer, "AuthorizeWithSpotify: ApiToken was empty.", http.StatusBadRequest)
64 | logger.LogErr("SpotifyAuthRequest Decoder", fmt.Errorf("ApiToken was empty"), request)
65 | return
66 | }
67 |
68 | spotifyMeReq, spotifyMeReqErr := http.NewRequest("GET", spotifyMeURI, nil)
69 | if spotifyMeReqErr != nil {
70 | http.Error(writer, spotifyMeReqErr.Error(), http.StatusInternalServerError)
71 | logger.LogErr("Request", spotifyMeReqErr, spotifyMeReq)
72 | return
73 | }
74 |
75 | log.Printf("Created SpotifyMeRequest %v", spotifyMeReq)
76 |
77 | // pheonix_d123 - "Client's gotta do what the Client's gotta do!" (02/26/20)
78 | spotifyMeReq.Header.Add("Authorization", "Bearer "+spotifyAuthRequest.APIToken)
79 |
80 | log.Printf("Added Headers tp SpotifyMeRequest %v", spotifyMeReq.Header)
81 |
82 | resp, httpErr := httpClient.Do(spotifyMeReq)
83 | if httpErr != nil {
84 | http.Error(writer, httpErr.Error(), http.StatusBadRequest)
85 | logger.LogErr("GET Request", httpErr, spotifyMeReq)
86 | return
87 | }
88 |
89 | // Check to see if request was valid
90 | if resp.StatusCode != http.StatusOK {
91 | log.Printf("SpotifyMeReq came back with code %v", resp.StatusCode)
92 |
93 | // Convert Spotify Error Object
94 | var spotifyErrorObj social.SpotifyRequestError
95 |
96 | err := json.NewDecoder(resp.Body).Decode(&spotifyErrorObj)
97 | if err != nil {
98 | http.Error(writer, err.Error(), http.StatusBadRequest)
99 | logger.LogErr("Spotify Request Decoder", err, spotifyMeReq)
100 | return
101 | }
102 |
103 | http.Error(writer, spotifyErrorObj.Error.Message, spotifyErrorObj.Error.Status)
104 | logger.LogErr("Spotify Request Decoder", fmt.Errorf(spotifyErrorObj.Error.Message), spotifyMeReq)
105 | return
106 | }
107 |
108 | var spotifyMeResponse social.SpotifyMeResponse
109 |
110 | // syszen - "wait that it? #easyGo"(02/27/20)
111 | // LilCazza - "Why the fuck doesn't this shit work" (02/27/20)
112 | respDecodeErr := json.NewDecoder(resp.Body).Decode(&spotifyMeResponse)
113 | if respDecodeErr != nil {
114 | http.Error(writer, respDecodeErr.Error(), http.StatusBadRequest)
115 | logger.LogErr("Spotify Request Decoder", respDecodeErr, spotifyMeReq)
116 | return
117 | }
118 |
119 | log.Printf("SpotifyMeReq was a success. Decoded response: %v", spotifyMeResponse)
120 |
121 | // Check DB for user, if there return user object
122 | authorizeWithSpotifyResp, userErr := getUser(spotifyMeResponse.ID)
123 | if userErr != nil {
124 | http.Error(writer, userErr.Error(), http.StatusBadRequest)
125 | logger.LogErr("GetUser", userErr, nil)
126 | return
127 | }
128 |
129 | log.Printf("Response from getUser check %v", authorizeWithSpotifyResp)
130 |
131 | // We do not have our user
132 | if authorizeWithSpotifyResp == nil && userErr == nil {
133 | log.Println("No user found. Need to create one.")
134 |
135 | // First, generate & write social platform object
136 | socialPlatDocRef, socialPlatData, socialPlatErr := createSocialPlatform(spotifyMeResponse, spotifyAuthRequest)
137 | if socialPlatErr != nil {
138 | http.Error(writer, socialPlatErr.Error(), http.StatusBadRequest)
139 | logger.LogErr("CreateSocialPlatform", socialPlatErr, nil)
140 | return
141 | }
142 |
143 | log.Printf("Social platform generated: %v", socialPlatDocRef)
144 |
145 | // Then, generate & write Firestore User object
146 | var firestoreUser, firestoreUserErr = createUser(spotifyMeResponse, socialPlatDocRef)
147 | if firestoreUserErr != nil {
148 | http.Error(writer, firestoreUserErr.Error(), http.StatusBadRequest)
149 | log.Printf("AuthorizeWithSpotify [createUser]: %v", firestoreUserErr)
150 | return
151 | }
152 |
153 | log.Printf("Firestore user generated: %v", firestoreUser)
154 |
155 | // Finally, get custom JWT
156 | var customToken, customTokenErr = getCustomToken(firestoreUser.ID)
157 | if customTokenErr != nil {
158 | http.Error(writer, customTokenErr.Error(), http.StatusBadRequest)
159 | logger.LogErr("CustomToken", customTokenErr, nil)
160 | return
161 | }
162 |
163 | log.Printf("Custom JWT Generated: %v", customToken)
164 |
165 | // sillyonly: "path.addLine(to: CGPoint(x: rect.width, y: rect.height))" (03/13/20)
166 | writer.WriteHeader(http.StatusOK)
167 | writer.Header().Set("Content-Type", "application/json")
168 | var spoitfyAuthResp = social.AuthorizeWithSpotifyResponse{
169 | Email: firestoreUser.Email,
170 | ID: firestoreUser.ID,
171 | Playlists: []firebase.FirestorePlaylist{},
172 | PreferredSocialPlatform: *socialPlatData,
173 | SocialPlatforms: []firebase.FirestoreSocialPlatform{*socialPlatData},
174 | Username: firestoreUser.Username,
175 | JWT: customToken.Token,
176 | }
177 |
178 | json.NewEncoder(writer).Encode(spoitfyAuthResp)
179 | return
180 | }
181 |
182 | // We have our user
183 | if authorizeWithSpotifyResp != nil {
184 | log.Println("User found!")
185 | // Still need to get our custom token here
186 | var customToken, customTokenErr = getCustomToken(authorizeWithSpotifyResp.ID)
187 | if customTokenErr != nil {
188 | http.Error(writer, customTokenErr.Error(), http.StatusBadRequest)
189 | logger.LogErr("CustomToken", customTokenErr, nil)
190 | return
191 | }
192 | authorizeWithSpotifyResp.JWT = customToken.Token
193 |
194 | log.Printf("Received token: %v", customToken.Token)
195 |
196 | writer.WriteHeader(http.StatusOK)
197 | writer.Header().Set("Content-Type", "application/json")
198 | json.NewEncoder(writer).Encode(authorizeWithSpotifyResp)
199 | }
200 |
201 | return
202 | }
203 |
204 | // no_neon_one - "BACKEND as a service" (02/29/20)
205 | // sillyonly - "still waiting on alecc to give me a discount" (02/29/20)
206 |
--------------------------------------------------------------------------------
/cmd/tokengen/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy generateCustomToken \
2 | --entry-point GenerateCustomToken \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/tokengen/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.7: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.6: Adding more logging & adding helpers file
3 | - v1.0.0-beta.5: Fixing merge
4 | - v1.0.0-beta.4: Reving firebase and social package to latest
5 | - v1.0.0-beta.3: Changing enviornment variable check location
6 | - v1.0.0-beta.2: Reving firebase and social package to latest
7 | - v1.0.0-beta.1: Reving firebase package to latest
--------------------------------------------------------------------------------
/cmd/tokengen/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/tokengen
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go v0.55.0 // indirect
7 | firebase.google.com/go v3.12.0+incompatible
8 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13 // indirect
9 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
10 | golang.org/x/tools v0.0.0-20200318150045-ba25ddc85566 // indirect
11 | google.golang.org/genproto v0.0.0-20200319113533-08878b785e9c // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/cmd/tokengen/helpers.go:
--------------------------------------------------------------------------------
1 | package tokengen
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | firebase "firebase.google.com/go"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | if os.Getenv("ENVIRONMENT") == "DEV" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
21 | hostname = os.Getenv("HOSTNAME_DEV")
22 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
23 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
24 | hostname = os.Getenv("HOSTNAME_PROD")
25 | }
26 |
27 | // Init Firebase App
28 | app, err := firebase.NewApp(context.Background(), nil)
29 | if err != nil {
30 | return fmt.Errorf("Firebase.NewApp: %v", err)
31 | }
32 |
33 | // Init Fireabase Auth Admin
34 | client, err = app.Auth(context.Background())
35 | if err != nil {
36 | return fmt.Errorf("Auth.Client: %v", err)
37 | }
38 |
39 | // Initialize Sawmill
40 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "AuthorizeWithSpotify")
41 | if err != nil {
42 | log.Printf("AuthorizeWithSpotify [Init Sawmill]: %v", err)
43 | }
44 |
45 | logger = sawmillLogger
46 |
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/cmd/tokengen/tokengen.go:
--------------------------------------------------------------------------------
1 | package tokengen
2 |
3 | // Remaiten - “Maybe now is the time to switch to Vue Native Kappa” (02/22/20)
4 | // BackeyM - "It's go time 🙃" (02/22/20)
5 | // TheDkbay - "It's double go time" (02/22/20)
6 | // LilCazza - "I can't see what's going on here, it may be because I can only hear" (02/24/20)
7 | // Tensei_c - "rust > go :P" (02/24/20)
8 | // pahnev - "swift > rust" (02/24/20)
9 | // jackconceprio - "I like pineapple juice any line" (02/25/20)
10 | // OnePocketPimp - "TODO: Create Dog Treat API to properly reward all good doggos on streaming platforms" (03/08/20)
11 | import (
12 | "context"
13 | "encoding/json"
14 | "log"
15 | "net/http"
16 |
17 | "firebase.google.com/go/auth"
18 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
19 | "github.com/pixelogicdev/gruveebackend/pkg/social"
20 | )
21 |
22 | var (
23 | client *auth.Client
24 | logger sawmill.Logger
25 | hostname string
26 | )
27 |
28 | func init() {
29 | log.Println("GenerateCustomToken initialized")
30 | }
31 |
32 | // GenerateCustomToken generates a CustomToken for Firebase Login
33 | func GenerateCustomToken(writer http.ResponseWriter, request *http.Request) {
34 | // Initialize
35 | initWithEnvErr := initWithEnv()
36 | if initWithEnvErr != nil {
37 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
38 | logger.LogErr("InitWithEnvErr", initWithEnvErr, nil)
39 | return
40 | }
41 |
42 | // If this is getting called, we have already authorized the user by verifying their API token is valid and pulls back their data
43 | var tokenRequest social.GenerateTokenRequest
44 |
45 | // Decode json from request
46 | err := json.NewDecoder(request.Body).Decode(&tokenRequest)
47 | if err != nil {
48 | log.Printf("json.NewDecoder: %v", err)
49 | http.Error(writer, err.Error(), http.StatusBadRequest)
50 | return
51 | }
52 |
53 | logger.Log("GenerateCustomToken", "Decoded request.")
54 |
55 | // Garahorn - "We need to generate the quantum GUID once the flux capacitor reaches terminal velocity." (02/24/20)
56 | token, err := client.CustomToken(context.Background(), tokenRequest.UID)
57 | if err != nil {
58 | log.Printf("client.CustomToken: %v", err)
59 | http.Error(writer, err.Error(), http.StatusBadRequest)
60 | return
61 | }
62 |
63 | logger.Log("GenerateCustomToken", "Successfully generated token.")
64 |
65 | // Create reponse object and pass it along
66 | tokenResponse := social.GenerateTokenResponse{Token: token}
67 | writer.Header().Set("Content-Type", "application/json")
68 | json.NewEncoder(writer).Encode(tokenResponse)
69 | }
70 |
--------------------------------------------------------------------------------
/cmd/updatealgolia/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy UpdateAlgolia \
2 | --runtime go113 \
3 | --trigger-event providers/cloud.firestore/eventTypes/document.create \
4 | --trigger-resource "projects/gruvee-3b7c4/databases/(default)/documents/users/{pushId}" \
5 | --env-vars-file ../../internal/config.yaml
--------------------------------------------------------------------------------
/cmd/updatealgolia/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.12: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.11: Adding more logging & adding types and helpers files
3 | - v1.0.0-beta.10: Fixing merge
4 | - v1.0.0-beta.9: Reving Firebase version
5 | - v1.0.0-beta.8: Adding DisplayName to payload
6 | - v1.0.0-beta.7: Updating objectID
7 | - v1.0.0-beta.6: Reving Firebase version for new mapvalues
8 | - v1.0.0-beta.5: Reving Firebase version
9 | - v1.0.0-beta.4: Reving Firebase version & adding log statements
10 | - v1.0.0-beta.3: Reving Firebase version and uploading imageUrl
11 | - v1.0.0-beta.2: Adding new firestore types & reving firebase version
12 | - v1.0.0-beta.1: Initializing function
--------------------------------------------------------------------------------
/cmd/updatealgolia/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/updatealgolia
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go v0.56.0
7 | github.com/algolia/algoliasearch-client-go/v3 v3.6.0
8 | github.com/kr/pretty v0.2.0 // indirect
9 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
10 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
11 | gopkg.in/yaml.v2 v2.2.8 // indirect
12 | )
13 |
--------------------------------------------------------------------------------
/cmd/updatealgolia/helpers.go:
--------------------------------------------------------------------------------
1 | package updatealgolia
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
8 | )
9 |
10 | // initWithEnv takes our yaml env variables and maps them properly.
11 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
12 | func initWithEnv() error {
13 | // Get paths
14 | var currentProject string
15 |
16 | if os.Getenv("ENVIRONMENT") == "DEV" {
17 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
18 | hostname = os.Getenv("HOSTNAME_DEV")
19 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
21 | hostname = os.Getenv("HOSTNAME_PROD")
22 | }
23 | // Initialize Sawmill
24 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "AuthorizeWithSpotify")
25 | if err != nil {
26 | log.Printf("AuthorizeWithSpotify [Init Sawmill]: %v", err)
27 | }
28 |
29 | logger = sawmillLogger
30 |
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/cmd/updatealgolia/types.go:
--------------------------------------------------------------------------------
1 | package updatealgolia
2 |
3 | // algoliaUser implements a partial amount of data from firestoreUser to use for indexing
4 | type algoliaUser struct {
5 | ObjectID string `json:"objectID"`
6 | ID string `json:"id"`
7 | Email string `json:"email"`
8 | ProfileImageURI string `json:"profileImage"`
9 | DisplayName string `json:"displayName"`
10 | Username string `json:"username"`
11 | }
12 |
--------------------------------------------------------------------------------
/cmd/updatealgolia/updatealgolia.go:
--------------------------------------------------------------------------------
1 | package updatealgolia
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/functions/metadata"
10 | "github.com/algolia/algoliasearch-client-go/v3/algolia/search"
11 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
12 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
13 | )
14 |
15 | var (
16 | logger sawmill.Logger
17 | hostname string
18 | )
19 |
20 | func init() {
21 | log.Println("UpdateAlgolia intialized")
22 | }
23 |
24 | // UpdateAlgolia sends new data to Algolia service for indexing
25 | func UpdateAlgolia(ctx context.Context, event firebase.FirestoreEvent) error {
26 | // Initialize
27 | initWithEnvErr := initWithEnv()
28 | if initWithEnvErr != nil {
29 | logger.LogErr("InitWithEnvErr", initWithEnvErr, nil)
30 | return initWithEnvErr
31 | }
32 |
33 | // Get IDs
34 | algoliaAppID := os.Getenv("ALGOLIA_APP_ID")
35 | if algoliaAppID == "" {
36 | error := fmt.Errorf("Algolia App ID was empty in yaml file")
37 | logger.LogErr("UpdateAlgolia", error, nil)
38 | return error
39 | }
40 |
41 | algoliaSecretID := os.Getenv("ALGOLIA_SECRET_ID")
42 | if algoliaSecretID == "" {
43 | error := fmt.Errorf("Algolia Secret ID was empty in yaml file")
44 | logger.LogErr("UpdateAlgolia", error, nil)
45 | return error
46 | }
47 |
48 | var algoliaIndexName string
49 | if os.Getenv("ENVIRONMENT") == "DEV" {
50 | algoliaIndexName = os.Getenv("ALGOLIA_INDEX_NAME_DEV")
51 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
52 | algoliaIndexName = os.Getenv("ALGOLIA_INDEX_NAME_PROD")
53 | }
54 |
55 | if algoliaIndexName == "" {
56 | error := fmt.Errorf("Algolia Index Name was empty in yaml file")
57 | logger.LogErr("UpdateAlgolia", error, nil)
58 | return error
59 | }
60 |
61 | // Init our client
62 | client := search.NewClient(algoliaAppID, algoliaSecretID)
63 | index := client.InitIndex(algoliaIndexName)
64 |
65 | meta, err := metadata.FromContext(ctx)
66 | if err != nil {
67 | error := fmt.Errorf("metadata.FromContext: %v", err)
68 | logger.LogErr("UpdateAlgolia", error, nil)
69 | return error
70 | }
71 |
72 | logger.Log("UpdateAlgolia", "Algolia clients created.")
73 |
74 | // Print out our trigger data
75 | logger.Log("UpdateAlgolia", fmt.Sprintf("Function triggered by change to: %v", meta.Resource))
76 | logger.Log("UpdateAlgolia", fmt.Sprintf("Event Trigger: %v", event))
77 |
78 | // Write objects to Algolia
79 | res, err := index.SaveObject(algoliaUser{
80 | ObjectID: event.Value.Fields.ID.StringValue,
81 | ID: event.Value.Fields.ID.StringValue,
82 | Email: event.Value.Fields.Email.StringValue,
83 | ProfileImageURI: event.Value.Fields.ProfileImage.MapValue.Fields.URL.StringValue,
84 | DisplayName: event.Value.Fields.DisplayName.StringValue,
85 | Username: event.Value.Fields.Username.StringValue,
86 | })
87 |
88 | logger.Log("UpdateAlgolia", fmt.Sprintf("SaveObject Res: %v", res))
89 |
90 | if err != nil {
91 | logger.LogErr("UpdateAlgolia", err, nil)
92 | return err
93 | }
94 |
95 | logger.Log("UpdateAlgolia", "Successfully wrote new user to Aloglia.")
96 |
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/cmd/usernameavailable/.deployment:
--------------------------------------------------------------------------------
1 | gcloud functions deploy usernameAvailable \
2 | --entry-point UsernameAvailable \
3 | --env-vars-file ../../internal/config.yaml \
4 | --runtime go113 \
5 | --allow-unauthenticated \
6 | --trigger-http
--------------------------------------------------------------------------------
/cmd/usernameavailable/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.3: Reving to trigger deployment (IGNORE)
2 | - v1.0.0-beta.2: Fixing merge
3 | - v1.0.0-beta.1: Initializing function
--------------------------------------------------------------------------------
/cmd/usernameavailable/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/cmd/usernameavailable
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.2.0
7 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
8 | )
9 |
--------------------------------------------------------------------------------
/cmd/usernameavailable/helpers.go:
--------------------------------------------------------------------------------
1 | package usernameavailable
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 |
9 | "cloud.google.com/go/firestore"
10 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
11 | )
12 |
13 | // initWithEnv takes our yaml env variables and maps them properly.
14 | // Unfortunately, we had to do this is main because in init we weren't able to access env variables
15 | func initWithEnv() error {
16 | // Get paths
17 | var currentProject string
18 |
19 | if os.Getenv("ENVIRONMENT") == "DEV" {
20 | currentProject = os.Getenv("FIREBASE_PROJECTID_DEV")
21 | } else if os.Getenv("ENVIRONMENT") == "PROD" {
22 | currentProject = os.Getenv("FIREBASE_PROJECTID_PROD")
23 | }
24 |
25 | // Initialize Firestore
26 | client, err := firestore.NewClient(context.Background(), currentProject)
27 | if err != nil {
28 | return fmt.Errorf("UsernameAvailable [Init Firestore]: %v", err)
29 | }
30 |
31 | // Initialize Sawmill
32 | sawmillLogger, err := sawmill.InitClient(currentProject, os.Getenv("GCLOUD_CONFIG"), os.Getenv("ENVIRONMENT"), "UsernameAvailable")
33 | if err != nil {
34 | log.Printf("UsernameAvailable [Init Sawmill]: %v", err)
35 | }
36 |
37 | firestoreClient = client
38 | logger = sawmillLogger
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/usernameavailable/types.go:
--------------------------------------------------------------------------------
1 | package usernameavailable
2 |
3 | // usernameAvailableReq includes the username to query the user collection
4 | type usernameAvailableReq struct {
5 | Username string `json:"username"`
6 | }
7 |
8 | // usernameAvailableResp includes a result of true or false
9 | type usernameAvailableResp struct {
10 | Result bool `json:"result"`
11 | }
12 |
--------------------------------------------------------------------------------
/cmd/usernameavailable/usernameavailable.go:
--------------------------------------------------------------------------------
1 | package usernameavailable
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "net/http"
9 |
10 | "cloud.google.com/go/firestore"
11 | "github.com/pixelogicdev/gruveebackend/pkg/sawmill"
12 | )
13 |
14 | var (
15 | firestoreClient *firestore.Client
16 | logger sawmill.Logger
17 | isUsernameAvailable bool
18 | )
19 |
20 | func init() {
21 | log.Println("UsernameAvailable intialized")
22 | }
23 |
24 | // UsernameAvailable checks to see if the given username is available to use
25 | func UsernameAvailable(writer http.ResponseWriter, request *http.Request) {
26 | // Initialize
27 | initWithEnvErr := initWithEnv()
28 | if initWithEnvErr != nil {
29 | http.Error(writer, initWithEnvErr.Error(), http.StatusInternalServerError)
30 | logger.LogErr("InitWithEnv", initWithEnvErr, nil)
31 | return
32 | }
33 |
34 | isUsernameAvailable = true
35 |
36 | // Get Username
37 | var reqData usernameAvailableReq
38 |
39 | reqDataErr := json.NewDecoder(request.Body).Decode(&reqData)
40 | if reqDataErr != nil {
41 | http.Error(writer, reqDataErr.Error(), http.StatusInternalServerError)
42 | logger.LogErr("ReqData Decoder", reqDataErr, request)
43 | return
44 | }
45 |
46 | logger.Log("UsernameAvailable", "Decoded request.")
47 |
48 | // Make a Firebase request to see if user document is already create with the given uid
49 | snapshots := firestoreClient.Collection("users").Where("username", "==", reqData.Username).Snapshots(context.Background())
50 | documents, documentsErr := snapshots.Query.Documents(context.Background()).GetAll()
51 | if documentsErr != nil {
52 | http.Error(writer, documentsErr.Error(), http.StatusInternalServerError)
53 | logger.LogErr("Firebase GetDocumentsQuery", documentsErr, request)
54 | return
55 | }
56 |
57 | logger.Log("UsernameAvailable", "Received snapshots.")
58 |
59 | if len(documents) > 0 {
60 | logger.Log("UsernameAvailable", fmt.Sprintf("%s has already been taken", reqData.Username))
61 | isUsernameAvailable = false
62 | }
63 |
64 | // Create result object
65 | result := usernameAvailableResp{
66 | Result: isUsernameAvailable,
67 | }
68 |
69 | logger.Log("UsernameAvailable", "Successfully created result.")
70 |
71 | // Send response
72 | writer.Header().Set("Content-Type", "application/json")
73 | json.NewEncoder(writer).Encode(result)
74 | }
75 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.3.0
7 | github.com/GoogleCloudPlatform/functions-framework-go v1.0.0
8 | github.com/cloudevents/sdk-go v1.1.2 // indirect
9 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
10 | github.com/joho/godotenv v1.3.0
11 | github.com/pixelogicdev/gruveebackend/cmd/appleauth v1.0.0-beta.3
12 | github.com/pixelogicdev/gruveebackend/cmd/createappledevtoken v1.0.0-beta.3
13 | github.com/pixelogicdev/gruveebackend/cmd/createprovideruser v1.0.0-beta.1
14 | github.com/pixelogicdev/gruveebackend/cmd/createsocialplaylist v1.0.0-beta.6
15 | github.com/pixelogicdev/gruveebackend/cmd/createuser v1.0.0-beta.2
16 | github.com/pixelogicdev/gruveebackend/cmd/doesuserdocexist v1.0.0-beta.1
17 | github.com/pixelogicdev/gruveebackend/cmd/fetchallmedia v1.0.0-beta.1
18 | github.com/pixelogicdev/gruveebackend/cmd/getapplemusicmedia v1.0.0-beta.1
19 | github.com/pixelogicdev/gruveebackend/cmd/getspotifymedia v1.0.0-beta.2
20 | github.com/pixelogicdev/gruveebackend/cmd/socialplatform v1.0.0-beta.2
21 | github.com/pixelogicdev/gruveebackend/cmd/socialtokenrefresh v1.0.0-beta.4
22 | github.com/pixelogicdev/gruveebackend/cmd/spotifyauth v1.0.0-beta.5
23 | github.com/pixelogicdev/gruveebackend/cmd/tokengen v1.0.0-beta.3
24 | github.com/pixelogicdev/gruveebackend/cmd/updatealgolia v1.0.0-beta.8
25 | github.com/pixelogicdev/gruveebackend/cmd/usernameavailable v1.0.0-beta.1
26 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
27 | github.com/pixelogicdev/gruveebackend/pkg/mediahelpers v1.0.0-beta.1
28 | github.com/pixelogicdev/gruveebackend/pkg/sawmill v1.0.0-beta.2
29 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
30 | google.golang.org/grpc v1.31.1
31 | )
32 |
33 | replace github.com/pixelogicdev/gruveebackend/cmd/appleauth => ./cmd/appleauth
34 |
35 | replace github.com/pixelogicdev/gruveebackend/cmd/spotifyauth => ./cmd/spotifyauth
36 |
37 | replace github.com/pixelogicdev/gruveebackend/cmd/tokengen => ./cmd/tokengen
38 |
39 | replace github.com/pixelogicdev/gruveebackend/cmd/socialplatform => ./cmd/socialplatform
40 |
41 | replace github.com/pixelogicdev/gruveebackend/cmd/createuser => ./cmd/createuser
42 |
43 | replace github.com/pixelogicdev/gruveebackend/cmd/socialtokenrefresh => ./cmd/socialtokenrefresh
44 |
45 | replace github.com/pixelogicdev/gruveebackend/cmd/createsocialplaylist => ./cmd/createsocialplaylist
46 |
47 | replace github.com/pixelogicdev/gruveebackend/cmd/updatealgolia => ./cmd/updatealgolia
48 |
49 | replace github.com/pixelogicdev/gruveebackend/cmd/fetchallmedia => ./cmd/fetchallmedia
50 |
51 | replace github.com/pixelogicdev/gruveebackend/cmd/getspotifymedia => ./cmd/getspotifymedia
52 |
53 | replace github.com/pixelogicdev/gruveebackend/cmd/getapplemusicmedia => ./cmd/getapplemusicmedia
54 |
55 | replace github.com/pixelogicdev/gruveebackend/cmd/createappledevtoken => ./cmd/createappledevtoken
56 |
57 | replace github.com/pixelogicdev/gruveebackend/cmd/doesuserdocexist => ./cmd/doesuserdocexist
58 |
59 | replace github.com/pixelogicdev/gruveebackend/cmd/usernameavailable => ./cmd/usernameavailable
60 |
61 | replace github.com/pixelogicdev/gruveebackend/cmd/createprovideruser => ./cmd/createprovideruser
62 |
63 | replace github.com/pixelogicdev/gruveebackend/pkg/firebase => ./pkg/firebase
64 |
65 | replace github.com/pixelogicdev/gruveebackend/pkg/social => ./pkg/social
66 |
67 | replace github.com/pixelogicdev/gruveebackend/pkg/mediahelpers => ./pkg/mediahelpers
68 |
69 | replace github.com/pixelogicdev/gruveebackend/pkg/sawmill => ./pkg/sawmill
70 |
71 | // WIP
72 | replace github.com/pixelogicdev/gruveebackend/internal/helpers/localcloudtrigger => ./internal/helpers/localcloudtrigger
73 |
--------------------------------------------------------------------------------
/internal/.gitkeep:
--------------------------------------------------------------------------------
1 | This folder should include:
2 | adminSdkSecret.json
3 | config.yaml
--------------------------------------------------------------------------------
/internal/helpers/localcloudtrigger/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/internal/helpers/localcloudtrigger
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/cloudevents/sdk-go v1.1.2
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.3
8 | )
9 |
10 | replace github.com/pixelogicdev/gruveebackend/pkg/firebase => ../../../pkg/firebase
11 |
--------------------------------------------------------------------------------
/internal/helpers/localcloudtrigger/localcloudtrigger.go:
--------------------------------------------------------------------------------
1 | package localcloudtrigger
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "log"
7 | "net/http"
8 |
9 | "github.com/cloudevents/sdk-go"
10 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
11 | )
12 |
13 | type localCloudTriggerRequest struct {
14 | EventID string `json:"eventId"`
15 | Data firebase.FirestoreEvent `json:"data"`
16 | Target string `json:"target"`
17 | Type string `json:"type"`
18 | }
19 |
20 | var eventSource = "http://localhost:8080/localCloudTrigger"
21 |
22 | // LocalCloudTrigger uses the CloudEvents SDK to trigger a Firebase Function Trigger Event locally
23 | func LocalCloudTrigger(writer http.ResponseWriter, request *http.Request) {
24 | // vezparsoftware - "R.I.P. Harambe" (03/28/20)
25 | var localCloudTriggerReq localCloudTriggerRequest
26 | requestErr := json.NewDecoder(request.Body).Decode(&localCloudTriggerReq)
27 | if requestErr != nil {
28 | http.Error(writer, requestErr.Error(), http.StatusInternalServerError)
29 | log.Printf("LocalCloudTrigger [localCloudTriggerReq Decoder]: %v", requestErr)
30 | return
31 | }
32 |
33 | log.Println(localCloudTriggerReq.Data)
34 |
35 | // Create new event
36 | event := cloudevents.NewEvent()
37 | event.SetID(localCloudTriggerReq.EventID)
38 | event.SetType(localCloudTriggerReq.Type)
39 | event.SetSource(eventSource)
40 | event.SetData(localCloudTriggerReq.Data)
41 |
42 | transport, transportErr := cloudevents.NewHTTPTransport(
43 | cloudevents.WithTarget(localCloudTriggerReq.Target),
44 | // TODO: Should try to verify which spec Firebase uses
45 | cloudevents.WithEncoding(cloudevents.HTTPBinaryV02),
46 | )
47 | if transportErr != nil {
48 | writer.WriteHeader(http.StatusInternalServerError)
49 | panic("failed to create transport, " + transportErr.Error())
50 | }
51 |
52 | client, clientErr := cloudevents.NewClient(transport)
53 | if clientErr != nil {
54 | writer.WriteHeader(http.StatusInternalServerError)
55 | panic("unable to create cloudevent client: " + clientErr.Error())
56 | }
57 |
58 | _, _, sendErr := client.Send(context.Background(), event)
59 | if sendErr != nil {
60 | writer.WriteHeader(http.StatusInternalServerError)
61 | panic("failed to send cloudevent: " + sendErr.Error())
62 | }
63 |
64 | writer.WriteHeader(http.StatusOK)
65 | }
66 |
67 | // curiousdrive - "Hakuna Matata"(03/28/20)
68 |
--------------------------------------------------------------------------------
/internal/mock-config.yaml:
--------------------------------------------------------------------------------
1 | # COMMON #
2 | ENVIRONMENT: DEV
3 | SPOTIFY_CLIENTID:
4 | SPOTIFY_SECRET:
5 | ALGOLIA_APP_ID:
6 | ALGOLIA_SECRET_ID:
7 |
8 | # DEV #
9 | HOSTNAME_DEV: http://localhost:8080
10 | FIREBASE_PROJECTID_DEV:
11 | ALGOLIA_INDEX_NAME_DEV:
12 | APPLE_AUTH_TEMPLATE_PATH_DEV: cmd/appleauth/templates
13 | APPLE_PRIVATE_KEY_PATH_DEV: cmd/createappledevtoken/apple_private_key.p8
14 | GCLOUD_CONFIG:
15 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // itshaydendev - "Firebase is bad but I use it anyway because I'm some kind of masochist" (04/26/20)
2 | package main
3 |
4 | // creativenobu - "compiled but feels interpreted (02/26/20)
5 | // pheonix_d123 - "Felt Compiled. Might interpret later" (02/26/20)
6 | // sillyonly - "YOU ALWAYS CLEAN MASTER BY FORCE PUSHING THE PERFECT CODE AND NOT THE CODE YOU WROTE" (02/23/20)
7 | // sillyonly - "OR PUSH AFTER AN APPROVED PR" (02/23/20)
8 | // no_neon_one - "have you tried Flutter?" (02/26/20)
9 | // MrDemonWolf - "A Furry was here OwO" (03/08/20)
10 | import (
11 | "log"
12 | "os"
13 |
14 | "github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
15 | "github.com/joho/godotenv"
16 | "github.com/pixelogicdev/gruveebackend/cmd/appleauth"
17 | "github.com/pixelogicdev/gruveebackend/cmd/createappledevtoken"
18 | "github.com/pixelogicdev/gruveebackend/cmd/createprovideruser"
19 | "github.com/pixelogicdev/gruveebackend/cmd/createsocialplaylist"
20 | "github.com/pixelogicdev/gruveebackend/cmd/createuser"
21 | "github.com/pixelogicdev/gruveebackend/cmd/doesuserdocexist"
22 | "github.com/pixelogicdev/gruveebackend/cmd/fetchallmedia"
23 | "github.com/pixelogicdev/gruveebackend/cmd/getapplemusicmedia"
24 | "github.com/pixelogicdev/gruveebackend/cmd/getspotifymedia"
25 | "github.com/pixelogicdev/gruveebackend/cmd/socialplatform"
26 | "github.com/pixelogicdev/gruveebackend/cmd/socialtokenrefresh"
27 | "github.com/pixelogicdev/gruveebackend/cmd/spotifyauth"
28 | "github.com/pixelogicdev/gruveebackend/cmd/tokengen"
29 | "github.com/pixelogicdev/gruveebackend/cmd/updatealgolia"
30 | "github.com/pixelogicdev/gruveebackend/cmd/usernameavailable"
31 | )
32 |
33 | func init() {
34 | // Load in ENV file
35 | goEnvErr := godotenv.Load("./internal/config.yaml")
36 | if goEnvErr != nil {
37 | log.Printf("Main [Load GoEnv]: %v\n", goEnvErr)
38 | }
39 | log.Println("Main environment variables loaded")
40 | }
41 |
42 | // InukApp - "Swift > Go" (03/15/20)
43 | // Fr3fou - "i helped build this AYAYA, follow @fr3fou on twitter uwu" (04/07/20)
44 | func main() {
45 | log.SetFlags(log.LstdFlags | log.Lshortfile)
46 |
47 | // General Endpoints
48 | funcframework.RegisterHTTPFunction("/authorizeWithApple", appleauth.AuthorizeWithApple)
49 | funcframework.RegisterHTTPFunction("/authorizeWithSpotify", spotifyauth.AuthorizeWithSpotify)
50 | funcframework.RegisterHTTPFunction("/generateCustomToken", tokengen.GenerateCustomToken)
51 | funcframework.RegisterHTTPFunction("/createSocialPlatform", socialplatform.CreateSocialPlatform)
52 | funcframework.RegisterHTTPFunction("/createUser", createuser.CreateUser)
53 | funcframework.RegisterHTTPFunction("/socialTokenRefresh", socialtokenrefresh.SocialTokenRefresh)
54 | funcframework.RegisterHTTPFunction("/createSocialPlaylist", createsocialplaylist.CreateSocialPlaylist)
55 | funcframework.RegisterHTTPFunction("/createAppleDevToken", createappledevtoken.CreateAppleDevToken)
56 | funcframework.RegisterHTTPFunction("/doesUserDocExist", doesuserdocexist.DoesUserDocExist)
57 | funcframework.RegisterHTTPFunction("/usernameAvailable", usernameavailable.UsernameAvailable)
58 | funcframework.RegisterHTTPFunction("/createProviderUser", createprovideruser.CreateProviderUser)
59 | funcframework.RegisterEventFunction("/updateAlgolia", updatealgolia.UpdateAlgolia)
60 | funcframework.RegisterEventFunction("/fetchAllMedia", fetchallmedia.FetchAllMedia)
61 |
62 | // Get Media Endpoints
63 | funcframework.RegisterHTTPFunction("/getSpotifyMedia", getspotifymedia.GetSpotifyMedia)
64 | funcframework.RegisterHTTPFunction("/getAppleMusicMedia", getapplemusicmedia.GetAppleMusicMedia)
65 |
66 | // WIP: Local trigger endpoint for cloud event
67 | // funcframework.RegisterHTTPFunction("/localCloudTrigger", localcloudtrigger.LocalCloudTrigger)
68 |
69 | // Use PORT environment variable, or default to 8080.
70 | port := "8080"
71 | if envPort := os.Getenv("PORT"); envPort != "" {
72 | port = envPort
73 | }
74 |
75 | if err := funcframework.Start(port); err != nil {
76 | log.Fatalf("funcframework.Start: %v\n", err)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/pkg/firebase/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.14: Making MediaMapValue exportable to compare in FetchAllMedia function
2 | - v1.0.0-beta.13: Updated and added shared types
3 | - v1.0.0-beta.12: Adding DisplayName to FirestoreUser and FirestoreEvent
4 | - v1.0.0-beta.11: Changing PlaylistsSongs type && FirestorePlaylist type
5 | - v1.0.0-beta.10: Adding FirestoreMedia
6 | - v1.0.0-beta.9: Adding MapValue props for image
7 | - v1.0.0-beta.8: Changing ProfileImage to utilize interface to check values
8 | - v1.0.0-beta.7: Tweaking FirestoreEventUser to utilize FirestoreEventProfileImage
9 | - v1.0.0-beta.6: Adding IntegerValue & ProfileImage for Algolia
10 | - v1.0.0-beta.5: Adding StringValue & FirestoreUserEvent types
11 | - v1.0.0-beta.4: Adding FirestoreEvent type
12 | - v1.0.0-beta.3: Moving social types to social package
13 | - v1.0.0-beta.2: Adding playlist type
14 | - v1.0.0-beta.1: Initial version commit with latest
--------------------------------------------------------------------------------
/pkg/firebase/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/pkg/firebase
2 |
3 | go 1.13
4 |
5 | require cloud.google.com/go/firestore v1.1.1
6 |
--------------------------------------------------------------------------------
/pkg/mediahelpers/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.1: Initializing package
--------------------------------------------------------------------------------
/pkg/mediahelpers/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/pkg/mediahelpers
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/firestore v1.3.0
7 | github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
8 | github.com/pixelogicdev/gruveebackend/pkg/social v1.0.0-beta.6
9 | google.golang.org/grpc v1.31.1
10 | )
11 |
--------------------------------------------------------------------------------
/pkg/mediahelpers/mediahelpers.go:
--------------------------------------------------------------------------------
1 | package mediahelpers
2 |
3 | import (
4 | "context"
5 | "encoding/base64"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | "strings"
13 | "time"
14 |
15 | "cloud.google.com/go/firestore"
16 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
17 | "github.com/pixelogicdev/gruveebackend/pkg/social"
18 | "google.golang.org/grpc/codes"
19 | "google.golang.org/grpc/status"
20 | )
21 |
22 | // -- CONSTANTS -- //
23 | var (
24 | spotifyAccessTokenURI = "https://accounts.spotify.com/api/token"
25 | httpClient = &http.Client{}
26 | )
27 |
28 | // -- TYPES -- //
29 |
30 | // SpotifyTrackData includes the reponse from a track in Spotify
31 | type SpotifyTrackData struct {
32 | ID string `json:"id"`
33 | Name string `json:"name"`
34 | Artists []SpotifyArtistData `json:"artists"`
35 | Type string `json:"type"`
36 | Album SpotifyAlbumData `json:"album"`
37 | ExternalURLs struct {
38 | Spotify string `json:"spotify"`
39 | } `json:"external_urls"`
40 | }
41 |
42 | // SpotifyArtistData includes the response from an artist in Spotify
43 | type SpotifyArtistData struct {
44 | ID string `json:"id"`
45 | Name string `json:"name"`
46 | Type string `json:"type"`
47 | ExternalURLs struct {
48 | Spotify string `json:"spotify"`
49 | } `json:"external_urls"`
50 | }
51 |
52 | // SpotifyAlbumData includes the response from an album in Spotify
53 | type SpotifyAlbumData struct {
54 | ID string `json:"id"`
55 | Name string `json:"name"`
56 | Type string `json:"type"`
57 | Artists []SpotifyArtistData `json:"artists"`
58 | Images []firebase.SpotifyImage `json:"images"`
59 | ExternalURLs struct {
60 | Spotify string `json:"spotify"`
61 | } `json:"external_urls"`
62 | }
63 |
64 | // -- HELPERS -- //
65 |
66 | // GetMediaFromFirestore takes in a firestoreClient, mediaProvider and the mediaId and checks to see if the media exists
67 | func GetMediaFromFirestore(firestoreClient firestore.Client, mediaProvider string, mediaID string) (*firebase.FirestoreMedia, error) {
68 | var queryString string
69 |
70 | switch mediaProvider {
71 | case "apple":
72 | log.Println("[GetMediaFromFirestore] Apple provider found.")
73 | queryString = "apple.id"
74 | case "spotify":
75 | log.Println("[GetMediaFromFirestore] Spotify provider found.")
76 | queryString = "spotify.id"
77 | default:
78 | return nil, fmt.Errorf("[GetMediaFromFirestore] Provider passed in was not found: %s", mediaProvider)
79 | }
80 |
81 | // Construct query
82 | query := firestoreClient.Collection("songs").Where(queryString, "==", mediaID)
83 |
84 | // Execute & check result length
85 | results, resultsErr := query.Documents(context.Background()).GetAll()
86 | if resultsErr != nil {
87 | return nil, fmt.Errorf("[GetMediaFromFirestore] Error getting all documents from query: %v", resultsErr)
88 | }
89 |
90 | // Song does exist, return document
91 | if len(results) > 0 {
92 | log.Println("[GetMediaFromFirestore] Found song document")
93 | bytes, bytesErr := json.Marshal(results[0].Data())
94 | if bytesErr != nil {
95 | return nil, fmt.Errorf("[GetMediaFromFirestore] Cannot marshal FirestoreMedia data: %v", bytesErr)
96 | }
97 |
98 | var firestoreMedia firebase.FirestoreMedia
99 | unmarhsalErr := json.Unmarshal(bytes, &firestoreMedia)
100 | if unmarhsalErr != nil {
101 | return nil, fmt.Errorf("[GetMediaFromFirestore] Cannot unmarshal bytes data: %v", unmarhsalErr)
102 | }
103 |
104 | return &firestoreMedia, nil
105 | }
106 |
107 | // Song does not exist, need to create new song document
108 | log.Println("[GetMediaFromFirestore] Did not find song document")
109 | return nil, nil
110 | }
111 |
112 | // FetchSpotifyAuthToken takes in a firestoreClient and returns a SpotifyAuthToken
113 | func FetchSpotifyAuthToken(firestoreClient firestore.Client) (*firebase.FirestoreSpotifyAuthToken, error) {
114 | // Go to Firebase and see if spotifyAuthToken exists
115 | snapshot, snapshotErr := firestoreClient.Collection("internal_tokens").Doc("spotifyAuthToken").Get(context.Background())
116 | if status.Code(snapshotErr) == codes.NotFound {
117 | log.Println("[GetSpotifyMedia] SpotifyAuthToken not found in DB. Need to create.")
118 | return nil, nil
119 | }
120 |
121 | if snapshotErr != nil {
122 | return nil, fmt.Errorf(snapshotErr.Error())
123 | }
124 |
125 | var spotifyAuthToken firebase.FirestoreSpotifyAuthToken
126 | dataToErr := snapshot.DataTo(&spotifyAuthToken)
127 | if dataToErr != nil {
128 | return nil, fmt.Errorf(dataToErr.Error())
129 | }
130 |
131 | log.Println("Checking to see if token needs to be refreshed")
132 |
133 | latestCreds, refreshAuthTokenErr := refreshSpotifyAuthToken(spotifyAuthToken, firestoreClient)
134 | if refreshAuthTokenErr != nil {
135 | return nil, refreshAuthTokenErr
136 | }
137 |
138 | if latestCreds != nil {
139 | return latestCreds, nil
140 | }
141 |
142 | // If we are here, no auth token was found
143 | newAuthToken, newAuthTokenErr := generateAuthToken()
144 | if newAuthTokenErr != nil {
145 | return nil, newAuthTokenErr
146 | }
147 |
148 | // Store new token in DB
149 | writeSpotifyAuthErr := writeSpotifyAuthtoken(*newAuthToken, firestoreClient)
150 | if writeSpotifyAuthErr != nil {
151 | return nil, writeSpotifyAuthErr
152 | }
153 |
154 | // TheYagich01: "Gejnerated" (08/11/20)
155 | log.Println("Generated auth token")
156 | return newAuthToken, nil
157 | }
158 |
159 | // refreshSpotifyAuthToken will check to see if Spotify AuthToken needs to be refreshed
160 | func refreshSpotifyAuthToken(authToken firebase.FirestoreSpotifyAuthToken, firestoreClient firestore.Client) (*firebase.FirestoreSpotifyAuthToken, error) {
161 | // Get current time
162 | var currentTime = time.Now()
163 |
164 | fmt.Printf("Expires In: %d seconds\n", authToken.ExpiresIn)
165 | fmt.Printf("Expires At: %s seconds\n", authToken.ExpiredAt)
166 |
167 | expiredAtTime, expiredAtTimeErr := time.Parse(time.RFC3339, authToken.ExpiredAt)
168 | if expiredAtTimeErr != nil {
169 | return nil, fmt.Errorf(expiredAtTimeErr.Error())
170 | }
171 |
172 | if currentTime.After(expiredAtTime) {
173 | fmt.Println("spotifyAuthToken is expired. Refreshing...")
174 | refreshToken, tokenActionErr := generateAuthToken()
175 | if tokenActionErr != nil {
176 | return nil, fmt.Errorf(tokenActionErr.Error())
177 | }
178 |
179 | // Set new token data in database
180 | writeTokenErr := writeSpotifyAuthtoken(*refreshToken, firestoreClient)
181 | if writeTokenErr != nil {
182 | return nil, fmt.Errorf(writeTokenErr.Error())
183 | }
184 |
185 | return refreshToken, nil
186 | }
187 |
188 | // Nothing is expired just return original token
189 | return &authToken, nil
190 | }
191 |
192 | // generateAuthToken will call Spotify creds service to get authToken for making requests
193 | func generateAuthToken() (*firebase.FirestoreSpotifyAuthToken, error) {
194 | // If not there generate new one and store
195 | authStr := os.Getenv("SPOTIFY_CLIENTID") + ":" + os.Getenv("SPOTIFY_SECRET")
196 |
197 | // Create Request
198 | data := url.Values{}
199 | data.Set("grant_type", "client_credentials")
200 |
201 | accessTokenReq, accessTokenReqErr := http.NewRequest("POST", spotifyAccessTokenURI,
202 | strings.NewReader(data.Encode()))
203 | if accessTokenReqErr != nil {
204 | return nil, fmt.Errorf(accessTokenReqErr.Error())
205 | }
206 |
207 | accessTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
208 | accessTokenReq.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(authStr)))
209 | accessTokenResp, accessTokenRespErr := httpClient.Do(accessTokenReq)
210 | if accessTokenRespErr != nil {
211 | return nil, fmt.Errorf(accessTokenRespErr.Error())
212 | }
213 |
214 | // Decode the token
215 | var spotifyClientCredsAuthResp social.SpotifyClientCredsAuthResp
216 | refreshTokenDecodeErr := json.NewDecoder(accessTokenResp.Body).Decode(&spotifyClientCredsAuthResp)
217 | if refreshTokenDecodeErr != nil {
218 | return nil, fmt.Errorf(refreshTokenDecodeErr.Error())
219 | }
220 |
221 | // Generate authToken for DB
222 | issuedAt := time.Now()
223 | expiresAt := issuedAt.Add(time.Second * time.Duration(spotifyClientCredsAuthResp.ExpiresIn))
224 | authToken := firebase.FirestoreSpotifyAuthToken{
225 | IssuedAt: issuedAt.Format(time.RFC3339),
226 | ExpiresIn: spotifyClientCredsAuthResp.ExpiresIn,
227 | ExpiredAt: expiresAt.Format(time.RFC3339),
228 | Token: spotifyClientCredsAuthResp.AccessToken,
229 | }
230 |
231 | return &authToken, nil
232 | }
233 |
234 | // writeSpotifyAuthtoken will take spotifyAuthToken and write it to the internal_tokens collection
235 | func writeSpotifyAuthtoken(authToken firebase.FirestoreSpotifyAuthToken, firestoreClient firestore.Client) error {
236 | spotifyAuthTokenDoc := firestoreClient.Collection("internal_tokens").Doc("spotifyAuthToken")
237 | if spotifyAuthTokenDoc == nil {
238 | return fmt.Errorf("spotifyAuthTokenDoc could not be found")
239 | }
240 |
241 | // MergeAll doesn't allow custom Go types so we need to create a map
242 | authMap := map[string]interface{}{
243 | "expiredAt": authToken.ExpiredAt,
244 | "expiresIn": authToken.ExpiresIn,
245 | "issuedAt": authToken.IssuedAt,
246 | "token": authToken.Token,
247 | }
248 |
249 | _, writeErr := spotifyAuthTokenDoc.Set(context.Background(), authMap, firestore.MergeAll)
250 | if writeErr != nil {
251 | return fmt.Errorf(writeErr.Error())
252 | }
253 |
254 | return nil
255 | }
256 |
--------------------------------------------------------------------------------
/pkg/sawmill/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.2: Updated Log function param order
2 | - v1.0.0-beta.1: Created Sawmill
--------------------------------------------------------------------------------
/pkg/sawmill/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/pkg/sawmill
2 |
3 | go 1.13
4 |
5 | require (
6 | cloud.google.com/go/logging v1.0.0
7 | google.golang.org/api v0.29.0
8 | google.golang.org/genproto v0.0.0-20200722002428-88e341933a54
9 | )
10 |
--------------------------------------------------------------------------------
/pkg/sawmill/sawmill.go:
--------------------------------------------------------------------------------
1 | package sawmill
2 |
3 | // If you're wondering why this package is named sawmill, I do too: https://clips.twitch.tv/PlacidOutstandingPelicanCharlieBitMe
4 | import (
5 | "context"
6 | "fmt"
7 | "log"
8 | "net/http"
9 |
10 | "cloud.google.com/go/logging"
11 | "google.golang.org/api/option"
12 | logpb "google.golang.org/genproto/googleapis/logging/v2"
13 | )
14 |
15 | // Logger holds the newly generated error logging client, so that it can be used for our own, more simple custom logging functions instead of the ones the Google provides
16 | type Logger struct {
17 | // The Logger
18 | GoogleLogger *logging.Logger
19 | // If their was an error with the initialization, it won't log to the cloud
20 | InitError bool
21 | // The service that logging was created for
22 | ServiceName string
23 | // If we are in the DEV environment, it won't log to the cloud
24 | isDevEnv bool
25 | }
26 |
27 | // InitClient creates a logging client
28 | func InitClient(projectID string, credentialsJSON string, environment string, serviceName string) (Logger, error) {
29 | if credentialsJSON == "" {
30 | return Logger{nil, true, "", false}, fmt.Errorf("Credentials ENV (found in config.yml) not set")
31 | }
32 |
33 | // The client takes the credentials in a byte array, so we do that conversion here
34 | credentialsByte := []byte(credentialsJSON)
35 | ctx := context.Background()
36 |
37 | // Initializes an Google logging client
38 | loggingClient, err := logging.NewClient(ctx, projectID, option.WithCredentialsJSON(credentialsByte))
39 | if err != nil {
40 | // If there is an error with initialization, it returns a Client object containing an empty ErrorClient, so nothing will get logged to the cloud, only the console
41 | return Logger{nil, true, "", false}, err
42 | }
43 |
44 | loggingClient.OnError = func(err error) {
45 | log.Printf("Logging error in "+serviceName+": %v", err)
46 | }
47 |
48 | // Creates the actual logger
49 | logger := loggingClient.Logger(serviceName)
50 |
51 | // If we are in a dev environment, we don't want to log to the cloud, so this variable is used as the value for InitError, and if is set to true, nothing will be logged to the cloud
52 | var isDevEnv bool = false
53 |
54 | if environment == "DEV" {
55 | isDevEnv = true
56 | }
57 |
58 | return Logger{logger, false, serviceName, isDevEnv}, nil
59 | }
60 |
61 | // This is an empty Entry struct, which is used to substitute in for any of the optional arguments that are not passed
62 | var emptyEntry logging.Entry
63 |
64 | // Log Just logs a standalone message to the console
65 | func (c Logger) Log(operation string, msg string) {
66 | // Logs to the terminal
67 | log.Printf("%s ["+operation+"]: %s ", c.ServiceName, msg)
68 | }
69 |
70 | // LogErr logs an error
71 | func (c Logger) LogErr(operation string, err error, req *http.Request) {
72 | // Logs the error to the terminal
73 | log.Printf("%s ["+operation+"]: %v ", c.ServiceName, err)
74 |
75 | // Checks if we are in the DEV environment
76 | if c.isDevEnv {
77 | return
78 | }
79 |
80 | // Checks if there was an error with the initialization of the Cloud Logging Client
81 | if c.InitError {
82 | return
83 | }
84 |
85 | requestStruct := emptyEntry.HTTPRequest
86 |
87 | // If req is passed, this sets requestStruct to a structure containing req
88 | if req != nil {
89 | requestStruct = &logging.HTTPRequest{
90 | Request: req,
91 | }
92 | }
93 |
94 | // Reports the error
95 | c.GoogleLogger.Log(logging.Entry{
96 | Payload: err.Error(),
97 | Operation: &logpb.LogEntryOperation{
98 | Id: operation,
99 | },
100 | HTTPRequest: requestStruct,
101 | })
102 |
103 | // Sends the log to the cloud
104 | c.GoogleLogger.Flush()
105 | }
106 |
--------------------------------------------------------------------------------
/pkg/social/.version:
--------------------------------------------------------------------------------
1 | - v1.0.0-beta.6: Adding AuthorizeWithSpotifyResponse & GetMediaReq
2 | - v1.0.0-beta.5: Adding AppleMusicError type
3 | - v1.0.0-beta.4: Adding DisplayName property to socialPlatform
4 | - v1.0.0-beta.3: Adding SpotifyClientCredsAuthResp
5 | - v1.0.0-beta.2: Replacing RefreshTokensResponse type with firebase.APIToken
6 | - v1.0.0-beta.1: Initializing package with social types
--------------------------------------------------------------------------------
/pkg/social/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/pixelogicdev/gruveebackend/pkg/social
2 |
3 | go 1.13
4 |
5 | require github.com/pixelogicdev/gruveebackend/pkg/firebase v1.0.0-beta.13
6 |
--------------------------------------------------------------------------------
/pkg/social/social.go:
--------------------------------------------------------------------------------
1 | package social
2 |
3 | import (
4 | "github.com/pixelogicdev/gruveebackend/pkg/firebase"
5 | )
6 |
7 | // -- SPOTIFY AUTH -- //
8 |
9 | // SpotifyAuthRequest includes APIToken needed for Spotify API
10 | type SpotifyAuthRequest struct {
11 | APIToken string `json:"token"`
12 | ExpiresIn int `json:"expiresIn"`
13 | RefreshToken string `json:"refreshToken"`
14 | }
15 |
16 | // SpotifyClientCredsAuthResp includes the response for a client credentials flow from Spotify
17 | type SpotifyClientCredsAuthResp struct {
18 | AccessToken string `json:"access_token"`
19 | TokenType string `json:"token_type"`
20 | ExpiresIn int `json:"expires_in"`
21 | }
22 |
23 | // SpotifyMeResponse represents the response coming back from the /me endpoint
24 | type SpotifyMeResponse struct {
25 | DisplayName string `json:"display_name"`
26 | Email string `json:"email"`
27 | ID string `json:"id"`
28 | Images []firebase.SpotifyImage `json:"images"`
29 | Product string `json:"product"`
30 | }
31 |
32 | // SpotifyRequestError represents the Spotify Error Object
33 | type SpotifyRequestError struct {
34 | Error spotifyRequestErrorDetails `json:"error"`
35 | }
36 |
37 | // SpotifyRequestErrorDetails represents the Spotify Error Object details
38 | type spotifyRequestErrorDetails struct {
39 | Status int `json:"status"`
40 | Message string `json:"message"`
41 | }
42 |
43 | // AuthorizeWithSpotifyResponse represents the data to send back to the client for a user
44 | type AuthorizeWithSpotifyResponse struct {
45 | Email string `json:"email"`
46 | ID string `json:"id"`
47 | Playlists []firebase.FirestorePlaylist `json:"playlists"`
48 | PreferredSocialPlatform firebase.FirestoreSocialPlatform `json:"preferredSocialPlatform"`
49 | SocialPlatforms []firebase.FirestoreSocialPlatform `json:"socialPlatforms"`
50 | Username string `json:"username"`
51 | JWT string `json:"jwt,omitempty"`
52 | }
53 |
54 | // -- APPLE MUSIC AUTH -- //
55 |
56 | // AppleMusicRequestError represents the Apple Music Error Object
57 | type AppleMusicRequestError struct {
58 | Errors []appleMusicError `json:"errors"`
59 | }
60 |
61 | // appleMusicError represents the Apple Music Error object data
62 | type appleMusicError struct {
63 | Code string `json:"code"`
64 | Detail string `json:"detail"`
65 | ID string `json:"id"`
66 | Status string `json:"status"`
67 | Title string `json:"title"`
68 | }
69 |
70 | // -- GENERATE TOKEN -- //
71 |
72 | // GenerateTokenRequest represents the UID for the user that we want to create a custom token for
73 | type GenerateTokenRequest struct {
74 | UID string
75 | }
76 |
77 | // GenerateTokenResponse represents what we will send back to the client
78 | type GenerateTokenResponse struct {
79 | Token string `json:"token"`
80 | }
81 |
82 | // -- REFRESH TOKEN -- //
83 |
84 | // TokenRefreshRequest includes uid to grab all social platforms for user
85 | type TokenRefreshRequest struct {
86 | UID string `json:"uid"`
87 | }
88 |
89 | // RefreshTokensResponse contains a list of refreshed tokens for multiple social platforms
90 | type RefreshTokensResponse struct {
91 | RefreshTokens map[string]firebase.APIToken `json:"refreshTokens"`
92 | }
93 |
94 | // RefreshToken contains the generic information for a refresh token for social platform
95 | type RefreshToken struct {
96 | PlatformName string `json:"platformName"`
97 | APIToken string `json:"access_token"`
98 | TokenType string `json:"token_type"`
99 | Scope string `json:"scope"`
100 | ExpiresIn int `json:"expires_in"`
101 | }
102 |
103 | // -- CREATE USER -- //
104 |
105 | // CreateUserReq is the payload that includes the minimal amount of data to create a user
106 | type CreateUserReq struct {
107 | Email string `json:"email"`
108 | ID string `json:"id"`
109 | SocialPlatformPath string `json:"socialPlatformPath"`
110 | ProfileImage *firebase.SpotifyImage `json:"profileImage"`
111 | DisplayName string `json:"displayName"`
112 | Username string `json:"username"`
113 | }
114 |
115 | // -- COMMON GET MEDIA -- //
116 |
117 | // GetMediaReq is the payload that inclues the provider, mediaId, and mediaType for finding media service
118 | type GetMediaReq struct {
119 | Provider string `json:"provider"`
120 | MediaID string `json:"mediaId"`
121 | MediaType string `json:"mediaType"`
122 | // This is only an Apple Music property so remove if not passed through
123 | Storefront string `json:"storefront,omitempty"`
124 | }
125 |
--------------------------------------------------------------------------------
/scripts/gorun.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This script is used for building and running the gruveebackend locally.
3 | # The main point of it is to append "replace" statements to all the mod files when building developing locally.
4 | # Currently it just dumps every firebase function into each mod file, but should be updated.
5 | # A deployment script for each firebase function should also be made that removes the replace lines.
6 |
7 | echo "Starting Go Run Script..."
8 |
9 | # Check if in root of project
10 | if [[ -f "main.go" ]]
11 | then
12 | # Variables
13 | googleCredsPath="./internal/adminSdkSecret-Dev.json"
14 |
15 | # These are the replace paths that are needed for shared packages.
16 | firebaseReplace="github.com/pixelogicdev/gruveebackend/pkg/firebase=../../pkg/firebase"
17 | socialReplace="github.com/pixelogicdev/gruveebackend/pkg/social=../../pkg/social"
18 | sawmillReplace="github.com/pixelogicdev/gruveebackend/pkg/sawmill=../../pkg/sawmill"
19 | mediaHelpersReplace="github.com/pixelogicdev/gruveebackend/pkg/mediahelpers=../../pkg/mediahelpers"
20 |
21 | # Add googleCreds to terminal instance
22 | export GOOGLE_APPLICATION_CREDENTIALS=$googleCredsPath
23 |
24 | # Go into each child directory and add replace to all mod files
25 | cd cmd
26 | for d in */
27 | do
28 | echo "Adding replace to $d"
29 | cd $d
30 |
31 | go mod edit -replace $firebaseReplace
32 | go mod edit -replace $socialReplace
33 | go mod edit -replace $sawmillReplace
34 | go mod edit -replace $mediaHelpersReplace
35 |
36 | # Move back up a directory
37 | cd ..
38 | done
39 |
40 | # Head back to main directory and run main
41 | cd ..
42 | go run main.go
43 | else
44 | echo "Make sure you are in the root of gruveebackend before running!"
45 | fi
46 |
--------------------------------------------------------------------------------