├── .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 | ![appleauth function push master](https://github.com/PixelogicDev/gruveebackend/workflows/appleauth%20function%20push%20master/badge.svg) 6 | ![createappledevtoken function push master](https://github.com/PixelogicDev/gruveebackend/workflows/createappledevtoken%20function%20push%20master/badge.svg) 7 | ![createprovideruser function push master](https://github.com/PixelogicDev/gruveebackend/workflows/createprovideruser%20function%20push%20master/badge.svg) 8 | ![createsocialplaylist function push master](https://github.com/PixelogicDev/gruveebackend/workflows/createsocialplaylist%20function%20push%20master/badge.svg) 9 | ![createuser function push master](https://github.com/PixelogicDev/gruveebackend/workflows/createuser%20function%20push%20master/badge.svg) 10 | ![doesuserdocexist function push master](https://github.com/PixelogicDev/gruveebackend/workflows/doesuserdocexist%20function%20push%20master/badge.svg) 11 | ![getspotifymedia function push master](https://github.com/PixelogicDev/gruveebackend/workflows/getspotifymedia%20function%20push%20master/badge.svg) 12 | ![socialplatform function push master](https://github.com/PixelogicDev/gruveebackend/workflows/socialplatform%20function%20push%20master/badge.svg) 13 | ![socialtokenrefresh function push master](https://github.com/PixelogicDev/gruveebackend/workflows/socialtokenrefresh%20function%20push%20master/badge.svg) 14 | ![spotifyauth function push master](https://github.com/PixelogicDev/gruveebackend/workflows/spotifyauth%20function%20push%20master/badge.svg) 15 | ![tokengen function push master](https://github.com/PixelogicDev/gruveebackend/workflows/tokengen%20function%20push%20master/badge.svg) 16 | ![updatealgolia function push master](https://github.com/PixelogicDev/gruveebackend/workflows/updatealgolia%20function%20push%20master/badge.svg) 17 | ![usernameavailable function push master](https://github.com/PixelogicDev/gruveebackend/workflows/usernameavailable%20function%20push%20master/badge.svg) 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 |
46 |
47 |
48 |
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 | --------------------------------------------------------------------------------