├── vercel.json
├── styles
└── tailwind.css
├── .eslintrc
├── public
└── favicon.ico
├── remix.env.d.ts
├── app
├── entry.client.tsx
├── entry.server.tsx
├── root.tsx
└── routes
│ └── index.tsx
├── server.js
├── tailwind.config.js
├── remix.config.js
├── tsconfig.json
├── .gitignore
├── package.json
├── go.mod
├── api
├── avatar.go
└── getpic.go
├── README.md
└── go.sum
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "github": {
3 | "silent": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node"]
3 | }
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DevClad-Inc/serverless-userpics/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/remix.env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/app/entry.client.tsx:
--------------------------------------------------------------------------------
1 | import { RemixBrowser } from "@remix-run/react";
2 | import { hydrateRoot } from "react-dom/client";
3 |
4 | hydrateRoot(document, );
5 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | import { createRequestHandler } from "@remix-run/vercel";
2 | import * as build from "@remix-run/dev/server-build";
3 |
4 | export default createRequestHandler({ build, mode: process.env.NODE_ENV });
5 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 |
3 | module.exports = {
4 | content: [
5 | "./app/**/*.{js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: ['@tailwindcss/forms', ],
11 | }
12 |
--------------------------------------------------------------------------------
/remix.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('@remix-run/dev').AppConfig} */
2 | module.exports = {
3 | serverBuildTarget: "vercel",
4 | // When running locally in development mode, we use the built in remix
5 | // server. This does not understand the vercel lambda module format,
6 | // so we default back to the standard build output.
7 | server: process.env.NODE_ENV === "development" ? undefined : "./server.js",
8 | ignoredRouteFiles: ["**/.*"],
9 | // appDirectory: "app",
10 | // assetsBuildDirectory: "public/build",
11 | // serverBuildPath: "api/index.js",
12 | // publicPath: "/build/",
13 | };
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
3 | "compilerOptions": {
4 | "lib": ["DOM", "DOM.Iterable", "ES2019"],
5 | "isolatedModules": true,
6 | "esModuleInterop": true,
7 | "jsx": "react-jsx",
8 | "moduleResolution": "node",
9 | "resolveJsonModule": true,
10 | "target": "ES2019",
11 | "strict": true,
12 | "allowJs": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "baseUrl": ".",
15 | "paths": {
16 | "~/*": ["./app/*"]
17 | },
18 |
19 | // Remix takes care of building everything in `remix build`.
20 | "noEmit": true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/entry.server.tsx:
--------------------------------------------------------------------------------
1 | import type { EntryContext } from "@remix-run/node";
2 | import { RemixServer } from "@remix-run/react";
3 | import { renderToString } from "react-dom/server";
4 |
5 | export default function handleRequest(
6 | request: Request,
7 | responseStatusCode: number,
8 | responseHeaders: Headers,
9 | remixContext: EntryContext
10 | ) {
11 | const markup = renderToString(
12 |
13 | );
14 |
15 | responseHeaders.set("Content-Type", "text/html");
16 |
17 | return new Response("" + markup, {
18 | status: responseStatusCode,
19 | headers: responseHeaders,
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/app/root.tsx:
--------------------------------------------------------------------------------
1 | import type { MetaFunction } from "@remix-run/node";
2 | import styles from "./styles/tailwind.css";
3 | import {
4 | Links,
5 | LiveReload,
6 | Meta,
7 | Outlet,
8 | Scripts,
9 | ScrollRestoration,
10 | } from "@remix-run/react";
11 |
12 | export function links() {
13 | return [
14 | { rel: "stylesheet",
15 | href: styles
16 | },
17 | {
18 | rel: "icon",
19 | href: "/favicon.ico",
20 | type: "image/ico",
21 | },
22 | ]
23 | }
24 |
25 | export const meta: MetaFunction = () => ({
26 | charset: "utf-8",
27 | title: "Userpics by DevClad - Avatars in 1 API Call",
28 | viewport: "width=device-width,initial-scale=1",
29 | });
30 |
31 | export default function App() {
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you prefer the allow list template instead of the deny list, see community template:
2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3 | #
4 | # Binaries for programs and plugins
5 | *.exe
6 | *.exe~
7 | *.dll
8 | *.so
9 | *.dylib
10 |
11 | # Test binary, built with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Dependency directories (remove the comment below to include it)
18 | # vendor/
19 |
20 | # Go workspace file
21 | go.work
22 |
23 | # Logs
24 | logs
25 | *.log
26 | npm-debug.log*
27 | yarn-debug.log*
28 | yarn-error.log*
29 | pnpm-debug.log*
30 | lerna-debug.log*
31 |
32 | node_modules
33 | dist
34 | dist-ssr
35 | *.local
36 |
37 | # Editor directories and files
38 | .vscode/*
39 | !.vscode/extensions.json
40 | .idea
41 | .DS_Store
42 | *.suo
43 | *.ntvs*
44 | *.njsproj
45 | *.sln
46 | *.sw?
47 |
48 | .cache
49 | .env
50 | .vercel
51 | .output
52 |
53 | /build/
54 | /public/build
55 | /api/index.js
56 | /api/index.js.map
57 |
58 | /app/styles/tailwind.css
59 | /api/_assets
60 | /api/index.js
61 | /api/index.js.map
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "sideEffects": false,
4 | "scripts": {
5 | "setup": "yarn vercel login && yarn vercel link",
6 | "start": "yarn vercel dev",
7 | "build": "yarn run build:css && remix build",
8 | "build:css": "tailwindcss -m -i ./styles/tailwind.css -o app/styles/tailwind.css",
9 | "dev": "concurrently \"yarn run dev:css\" \"remix dev\"",
10 | "dev:css": "tailwindcss -w -i ./styles/tailwind.css -o app/styles/tailwind.css"
11 | },
12 | "dependencies": {
13 | "@heroicons/react": "^2.0.11",
14 | "@remix-run/node": "^1.7.2",
15 | "@remix-run/react": "^1.7.2",
16 | "@remix-run/vercel": "^1.7.2",
17 | "@vercel/node": "^2.4.4",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0"
20 | },
21 | "devDependencies": {
22 | "@remix-run/dev": "^1.7.2",
23 | "@remix-run/eslint-config": "^1.7.2",
24 | "@remix-run/serve": "^1.7.2",
25 | "@types/react": "^18.0.15",
26 | "@types/react-dom": "^18.0.6",
27 | "autoprefixer": "^10.4.12",
28 | "concurrently": "^7.4.0",
29 | "eslint": "^8.23.1",
30 | "postcss": "^8.4.16",
31 | "tailwindcss": "^3.1.8",
32 | "typescript": "^4.7.4"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/arthtyagi/serverless-userpics
2 |
3 | // go 1.19
4 | go 1.18
5 |
6 | require (
7 | github.com/aws/aws-sdk-go-v2 v1.16.16
8 | github.com/aws/aws-sdk-go-v2/config v1.17.7
9 | github.com/aws/aws-sdk-go-v2/credentials v1.12.20
10 | github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11
11 | )
12 |
13 | require (
14 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect
15 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect
16 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect
17 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect
18 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect
19 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 // indirect
20 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 // indirect
21 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect
22 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect
23 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect
24 | github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect
25 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 // indirect
26 | github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 // indirect
27 | github.com/aws/smithy-go v1.13.3 // indirect
28 | )
29 |
30 | // ignore node_modules
31 |
--------------------------------------------------------------------------------
/api/avatar.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "log"
7 | "net/http"
8 | "strconv"
9 | "sync"
10 | )
11 |
12 | type BoringParams struct {
13 | size int
14 | name string
15 | variant string
16 | }
17 |
18 | func Boring (w http.ResponseWriter, r *http.Request) {
19 |
20 | base_url := "https://source.boringavatars.com/"
21 |
22 | // default values
23 | size := 128
24 | name := "Cactus Jack"
25 | variant := "beam"
26 |
27 | stream := false
28 |
29 | if r.URL.Query().Get("size") != "" {
30 | s, err := strconv.Atoi(r.URL.Query().Get("size"))
31 | if err != nil {
32 | log.Fatal(err)
33 | } else {
34 | size = s
35 | }
36 | }
37 | if r.URL.Query().Get("name") != "" {
38 | name = r.URL.Query().Get("name")
39 | }
40 | if r.URL.Query().Get("variant") != "" {
41 | variant = r.URL.Query().Get("variant")
42 | }
43 |
44 | if r.URL.Query().Get("stream") != "" {
45 | stream = true
46 | }
47 |
48 | // todo: add color support
49 | params := BoringParams{
50 | size: size,
51 | name: name,
52 | variant: variant,
53 | }
54 | // why a waitgroup? cause fuck you, i like goroutines
55 | wg := sync.WaitGroup{}
56 | wg.Add(1)
57 |
58 | go func() {
59 | defer wg.Done()
60 | if stream {
61 | // display the image
62 | StreamBoringPic(w, r, FetchBoring(params, base_url))
63 | } else {
64 | fmt.Fprintln(w, "URL:", FetchBoring(params, base_url))
65 | fmt.Fprintf(w, "Query Params: size=%d, name=%s, variant=%s, stream=%t", size, name, variant, stream)
66 | }
67 | }()
68 |
69 | wg.Wait()
70 |
71 | }
72 |
73 | func FetchBoring (params BoringParams, base_url string) string {
74 |
75 | base_url += params.variant + "/" + strconv.Itoa(params.size) + "/" + params.name
76 |
77 | return base_url
78 | }
79 |
80 | func StreamBoringPic (w http.ResponseWriter, r *http.Request, url string) int64 {
81 | // download the image
82 | resp, err := http.Get(url)
83 | if err != nil {
84 | log.Fatal(err)
85 | }
86 |
87 | // stream the image
88 | w.Header().Set("Content-Type", "image/svg")
89 |
90 | res, err := io.Copy(w, resp.Body)
91 |
92 | if err != nil {
93 | log.Fatal(err)
94 | } else {
95 | return res
96 | }
97 |
98 | return res
99 | }
--------------------------------------------------------------------------------
/app/routes/index.tsx:
--------------------------------------------------------------------------------
1 | export default function Index() {
2 | return (
3 |
4 |
5 |
Serverless Userpics
6 |
7 |
26 |
27 |
Instructions
28 |
29 |
30 | Please check out the
31 |
35 | readme
36 |
37 | on GitHub for instructions on how to use this. (It's easy!)
38 |
39 |
40 |
41 |
53 |
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/api/getpic.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "math/rand"
9 | "net/http"
10 | "os"
11 |
12 | // "path/filepath"
13 | "sync"
14 | "time"
15 |
16 | // external
17 | "github.com/aws/aws-sdk-go-v2/aws"
18 | "github.com/aws/aws-sdk-go-v2/config"
19 | "github.com/aws/aws-sdk-go-v2/credentials"
20 | "github.com/aws/aws-sdk-go-v2/service/s3"
21 | // "github.com/joho/godotenv"
22 | )
23 |
24 | type S3Response struct {
25 | Contents []struct {
26 | Key string `json:"Key"`
27 | } `json:"Contents"`
28 | }
29 |
30 | // uncomment some code to test locally
31 |
32 | // func main() {
33 | // GetPic()
34 | // }
35 |
36 | func GetPic(w http.ResponseWriter, r *http.Request) {
37 | // err := godotenv.Load(filepath.Join("api/.env"))
38 | // fmt.Println("Loading .env file", err)
39 | // if err != nil { log.Fatal("Error loading .env file") }
40 |
41 | // ENV INITIALIZATION
42 | accountId := os.Getenv("ACCOUNT_ID")
43 | bucketName := os.Getenv("BUCKET_NAME")
44 | accessKeyId := os.Getenv("API_ACCESS_KEY")
45 | accessKeySecret := os.Getenv("API_SECRET_KEY")
46 |
47 | // === initialization shit ===
48 |
49 | // AWS CONFIGURATION (using V2 SDK - https://developers.cloudflare.com/r2/examples/aws-sdk-go/ gives a neat example btw)
50 | r2Resolver := aws.EndpointResolverWithOptionsFunc(
51 | func(service, region string, options ...interface{},
52 | ) (aws.Endpoint, error) {
53 | return aws.Endpoint{
54 | URL: fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountId),
55 | }, nil
56 | })
57 |
58 |
59 | cfg, err := config.LoadDefaultConfig(context.TODO(),
60 | config.WithEndpointResolverWithOptions(r2Resolver),
61 | config.WithCredentialsProvider(
62 | credentials.NewStaticCredentialsProvider(
63 | accessKeyId, accessKeySecret, "",
64 | ),
65 | ),
66 | )
67 | if err != nil {log.Fatal(err)}
68 |
69 | client := s3.NewFromConfig(cfg)
70 |
71 | // test out
72 | // fmt.Println(ImageList(client, bucketName, accountId))
73 |
74 | wg := sync.WaitGroup{}
75 | wg.Add(1)
76 | go func() {
77 | defer wg.Done()
78 | fmt.Fprintf(w, ImageList(client, bucketName, accountId))
79 | // ImageList(client, bucketName, accountId)
80 | }()
81 |
82 | wg.Wait()
83 |
84 | }
85 |
86 | func ImageList(client *s3.Client, bucketName string, accountId string) string {
87 | publicId := os.Getenv("PUBLIC_ID")
88 | // LIST OF IMAGES
89 | // (https://developers.cloudflare.com/r2/data-access/s3-api/api/#implemented-object-level-operations/) has list of available operations
90 |
91 | input := &s3.ListObjectsV2Input{
92 | Bucket: aws.String(bucketName),
93 | }
94 |
95 | result, err := client.ListObjectsV2(context.TODO(), input)
96 | if err != nil {log.Fatal(err)}
97 |
98 | // RESPONSE HANDLING
99 |
100 | var s3Response S3Response
101 | jsonString, err := json.Marshal(result)
102 | if err != nil {log.Fatal(err)}
103 | json.Unmarshal(jsonString, &s3Response)
104 |
105 | // i have to use time as a source?! lol python is better hAhAhA (ofc not wtf)
106 | goddamnSource := rand.NewSource(time.Now().UnixNano())
107 | r := rand.New(goddamnSource)
108 | randomNumber := r.Intn(len(s3Response.Contents))
109 | randomImage := s3Response.Contents[randomNumber].Key
110 | generatedURl := fmt.Sprintf("https://%s.r2.dev/%s", publicId, randomImage)
111 | return generatedURl
112 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚀 Serverless Userpics
2 |
3 | **Fetch avatars for your next service in a single API call.**
4 |
5 | Development has moved to the [DevClad Mono[turbo]repo](https://github.com/DevClad-Inc/devclad/tree/main/apps/userpics)
6 |
7 | ## Features
8 |
9 | - [x] 🚀 Generate avatars on the fly
10 | - [X] 🎨 100 avatars included from [Craftwork.design](https://craftwork.design/) (I'm not affiliated with them, just a fan)
11 | - [X] 🎨 Abstraction over [Boring Avatars](https://boringavatars.com/) included as well.
12 | - [x] ✨ Tiny as fuck.
13 | - [x] ✨ Serverless. Configurable. Easily deployable.
14 |
15 | ## Usage (Important)
16 |
17 | ### Routes
18 |
19 | 1. `api/getpic/`
20 | 2. `api/avatar/` - `?stream=True` query outputs an SVG for you.
21 |
22 | #### Available Query Params
23 |
24 | - `?name=string` - Your username. Defaults to `Cactus Jack`.
25 | - `?size=int` - Size of the avatar. Default is 128.
26 | - `?variant=string` - Variant of the avatar. Default is `beam`.
27 | - `?stream=bool` - Stream the SVG instead of returning a URL. Default is False.
28 |
29 | #### Available Variants
30 |
31 | - `marble`
32 | - `pixel`
33 | - `beam`
34 | - `sunset`
35 | - `ring`
36 | - `bauhaus`
37 |
38 | All these variants and avatars (under `api/avatar/` route) are from [Boring Avatars](https://boringavatars.com/).
39 |
40 | 
41 |
42 | ### Quick and dirty example
43 |
44 | 1. Head over to [userpics.devclad.com](https://userpics.devclad.com) and "Get a random user pic".
45 | 2. Use the URL in your app.
46 |
47 | ### Python Example
48 |
49 | - `/api/getpic/` is the endpoint to get a random avatar.
50 |
51 | ``` python
52 | def random_avatar():
53 | name = str(uuid.uuid4())[:8]
54 | with open(f"media/avatars/{name}.png", "wb+") as f:
55 | url = requests.get("https://userpics.devclad.com/api/getpic")
56 | response = requests.get(url.text, stream=True)
57 | if not response.ok:
58 | raise Exception("Could not get avatar")
59 | for block in response.iter_content(1024):
60 | if not block:
61 | break
62 | f.write(block)
63 | return f"avatars/{name}.png"
64 | ```
65 |
66 | You can try svg too btw, haven't tested it but it should work.
67 |
68 | ## Config
69 |
70 | ``` go
71 | accountId := os.Getenv("ACCOUNT_ID")
72 | bucketName := os.Getenv("BUCKET_NAME")
73 | accessKeyId := os.Getenv("API_ACCESS_KEY")
74 | accessKeySecret := os.Getenv("API_SECRET_KEY")
75 | ```
76 |
77 | ### Replicate this
78 |
79 | 1. Create an R2 bucket on Cloudflare.
80 | 2. Generate S3 Token via `Manage R2 API Tokens` in R2 Dashboard.
81 | 3. Set your environment variables.
82 | 4. Deploy ⚡
83 |
84 | ### Running locally
85 |
86 | do the usual. install dependencies via `yarn` and run `yarn run start`/`yarn run dev`.
87 |
88 | **Make sure to setup environment variables in your Vercel dashboard.**
89 | **Also make sure to have S3 API access and secret on the recieving end if your bucket is private like mine.**
90 |
91 | ### Services
92 |
93 | | Resource | Service |
94 | | --- | --- |
95 | | Storage | Cloudflare R2 (S3 compatible) |
96 | | Serverless Hosting | Vercel (AWS Lambda) |
97 |
98 | ---
99 |
100 | ### Todo
101 |
102 | Check out the [issues](https://github.com/arthtyagi/serverless-userpics/issues) for more info.
103 |
104 | #### YANKING
105 |
106 | [template-go-vercel](https://github.com/riccardogiorato/template-go-vercel) was helpful while deploying.
107 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk=
2 | github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k=
3 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 h1:tcFliCWne+zOuUfKNRn8JdFBuWPDuISDH08wD2ULkhk=
4 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8/go.mod h1:JTnlBSot91steJeti4ryyu/tLd4Sk84O5W22L7O2EQU=
5 | github.com/aws/aws-sdk-go-v2/config v1.17.7 h1:odVM52tFHhpqZBKNjVW5h+Zt1tKHbhdTQRb+0WHrNtw=
6 | github.com/aws/aws-sdk-go-v2/config v1.17.7/go.mod h1:dN2gja/QXxFF15hQreyrqYhLBaQo1d9ZKe/v/uplQoI=
7 | github.com/aws/aws-sdk-go-v2/credentials v1.12.20 h1:9+ZhlDY7N9dPnUmf7CDfW9In4sW5Ff3bh7oy4DzS1IE=
8 | github.com/aws/aws-sdk-go-v2/credentials v1.12.20/go.mod h1:UKY5HyIux08bbNA7Blv4PcXQ8cTkGh7ghHMFklaviR4=
9 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 h1:r08j4sbZu/RVi+BNxkBJwPMUYY3P8mgSDuKkZ/ZN1lE=
10 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ=
11 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 h1:s4g/wnzMf+qepSNgTvaQQHNxyMLKSawNhKCPNy++2xY=
12 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4=
13 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 h1:/K482T5A3623WJgWT8w1yRAFK4RzGzEl7y39yhtn9eA=
14 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg=
15 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 h1:wj5Rwc05hvUSvKuOF29IYb9QrCLjU+rHAy/x/o0DK2c=
16 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8=
17 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 h1:ZSIPAkAsCCjYrhqfw2+lNzWDzxzHXEckFkTePL5RSWQ=
18 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14/go.mod h1:AyGgqiKv9ECM6IZeNQtdT8NnMvUb3/2wokeq2Fgryto=
19 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 h1:Lh1AShsuIJTwMkoxVCAYPJgNG5H+eN6SmoUn8nOZ5wE=
20 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw=
21 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 h1:BBYoNQt2kUZUUK4bIPsKrCcjVPUMNsgQpNAwhznK/zo=
22 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18/go.mod h1:NS55eQ4YixUJPTC+INxi2/jCqe1y2Uw3rnh9wEOVJxY=
23 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 h1:Jrd/oMh0PKQc6+BowB+pLEwLIgaQF29eYbe7E1Av9Ug=
24 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI=
25 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 h1:HfVVR1vItaG6le+Bpw6P4midjBDMKnjMyZnw9MXYUcE=
26 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI=
27 | github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw=
28 | github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo=
29 | github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 h1:pwvCchFUEnlceKIgPUouBJwK81aCkQ8UDMORfeFtW10=
30 | github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80=
31 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 h1:GUnZ62TevLqIoDyHeiWj2P7EqaosgakBKVvWriIdLQY=
32 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA=
33 | github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 h1:9pPi0PsFNAGILFfPCk8Y0iyEBGc6lu6OQ97U7hmdesg=
34 | github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM=
35 | github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA=
36 | github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
37 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
38 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
39 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
40 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
41 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
42 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
43 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
45 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
46 |
--------------------------------------------------------------------------------