├── .gitignore ├── .ignore ├── README.md ├── assets ├── embed.go ├── public │ └── html │ │ ├── footer.tmpl │ │ ├── index.html │ │ └── navbar.tmpl └── static │ └── style.css ├── check.sh ├── common ├── common.go ├── config.go └── timeago.go ├── config.toml ├── data ├── data.go ├── main.go └── models.go ├── gitleaks.toml ├── go.mod ├── go.sum ├── handlers ├── healthcheck.go ├── helper.go ├── index.go ├── route.go └── template.go ├── main.go ├── service ├── mapcache.go └── service.go └── unique_code.py /.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 | .idea 24 | -------------------------------------------------------------------------------- /.ignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-Http-Template 2 | 3 | Opinionated template of how I organise Go code to create HTTP webservices. 4 | 5 | Use if you like, or don't. If you do you will want to replace the name "changeme" as the package. -------------------------------------------------------------------------------- /assets/embed.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | //go:embed public 8 | var Assets embed.FS 9 | -------------------------------------------------------------------------------- /assets/public/html/footer.tmpl: -------------------------------------------------------------------------------- 1 | {{ define "footer" }} 2 |
3 |
4 |
5 | 10 |
11 |
12 |
13 | {{ end }} -------------------------------------------------------------------------------- /assets/public/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Sample 9 | 10 | 11 | 12 | {{ template "navbar" }} 13 | 14 |
15 |
16 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

17 |
18 |
19 | 20 |
21 |
22 |

Suspendisse nec eros volutpat, euismod quam a, iaculis augue. Nam enim ipsum, condimentum ac mattis quis, gravida sit amet ipsum. Nulla posuere, libero vel fermentum convallis, dolor risus lacinia sapien, commodo mattis ligula justo in arcu. Nam vitae lacinia metus. Vivamus a venenatis sapien. Nulla in metus turpis. Nam sed tortor ornare, interdum libero at, dapibus est. Suspendisse ultricies metus id justo feugiat cursus. Phasellus sit amet finibus mauris, ut tincidunt odio. Vestibulum hendrerit interdum dignissim. Pellentesque quis sapien id nisi faucibus molestie. Vestibulum laoreet congue molestie. Pellentesque malesuada sapien ut cursus sodales. Quisque et odio eu eros hendrerit hendrerit sed posuere erat.

23 |
24 |
25 | 26 |
27 |
28 |

Suspendisse porttitor lorem at suscipit fringilla. Nulla ultricies mi ut consequat luctus. Nullam eget purus justo. Donec ultrices, nibh quis varius vulputate, elit nisi fermentum libero, id viverra justo libero et orci. Aenean sagittis purus a dui malesuada placerat. Pellentesque eros nibh, accumsan at nisl sit amet, tincidunt dapibus tortor. Aliquam erat volutpat. Ut porttitor metus velit, nec venenatis turpis imperdiet in. In eu pretium tortor. Nam quis sapien at lacus eleifend sodales. Cras ante nunc, dignissim sed consequat ac, aliquet sed arcu. Donec ultricies elit eget vestibulum euismod. Nullam imperdiet leo a est luctus, sit amet luctus neque rhoncus. Nam vel semper enim.

29 |
30 |
31 | 32 | {{ template "footer" }} 33 | 34 | 35 | -------------------------------------------------------------------------------- /assets/public/html/navbar.tmpl: -------------------------------------------------------------------------------- 1 | {{ define "navbar" }} 2 | 14 | {{ end }} -------------------------------------------------------------------------------- /assets/static/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } 350 | 351 | /* 352 | * Skeleton V2.0.4 353 | * Copyright 2014, Dave Gamache 354 | * www.getskeleton.com 355 | * Free to use under the MIT license. 356 | * http://www.opensource.org/licenses/mit-license.php 357 | * 12/29/2014 358 | */ 359 | 360 | 361 | /* Table of contents 362 | –––––––––––––––––––––––––––––––––––––––––––––––––– 363 | - Grid 364 | - Base Styles 365 | - Typography 366 | - Links 367 | - Buttons 368 | - Forms 369 | - Lists 370 | - Code 371 | - Tables 372 | - Spacing 373 | - Utilities 374 | - Clearing 375 | - Media Queries 376 | */ 377 | 378 | 379 | /* Grid 380 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 381 | .container { 382 | position: relative; 383 | width: 100%; 384 | /*max-width: 960px;*/ 385 | margin: 0 auto; 386 | padding: 0 20px; 387 | box-sizing: border-box; } 388 | .column, 389 | .columns { 390 | width: 100%; 391 | float: left; 392 | box-sizing: border-box; } 393 | 394 | /* For devices larger than 400px */ 395 | @media (min-width: 400px) { 396 | .container { 397 | width: 85%; 398 | padding: 0; } 399 | } 400 | 401 | /* For devices larger than 550px */ 402 | @media (min-width: 550px) { 403 | .container { 404 | width: 80%; } 405 | .column, 406 | .columns { 407 | margin-left: 4%; } 408 | .column:first-child, 409 | .columns:first-child { 410 | margin-left: 0; } 411 | 412 | .one.column, 413 | .one.columns { width: 4.66666666667%; } 414 | .two.columns { width: 13.3333333333%; } 415 | .three.columns { width: 22%; } 416 | .four.columns { width: 30.6666666667%; } 417 | .five.columns { width: 39.3333333333%; } 418 | .six.columns { width: 48%; } 419 | .seven.columns { width: 56.6666666667%; } 420 | .eight.columns { width: 65.3333333333%; } 421 | .nine.columns { width: 74.0%; } 422 | .ten.columns { width: 82.6666666667%; } 423 | .eleven.columns { width: 91.3333333333%; } 424 | .twelve.columns { width: 100%; margin-left: 0; } 425 | 426 | .one-third.column { width: 30.6666666667%; } 427 | .two-thirds.column { width: 65.3333333333%; } 428 | 429 | .one-half.column { width: 48%; } 430 | 431 | /* Offsets */ 432 | .offset-by-one.column, 433 | .offset-by-one.columns { margin-left: 8.66666666667%; } 434 | .offset-by-two.column, 435 | .offset-by-two.columns { margin-left: 17.3333333333%; } 436 | .offset-by-three.column, 437 | .offset-by-three.columns { margin-left: 26%; } 438 | .offset-by-four.column, 439 | .offset-by-four.columns { margin-left: 34.6666666667%; } 440 | .offset-by-five.column, 441 | .offset-by-five.columns { margin-left: 43.3333333333%; } 442 | .offset-by-six.column, 443 | .offset-by-six.columns { margin-left: 52%; } 444 | .offset-by-seven.column, 445 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 446 | .offset-by-eight.column, 447 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 448 | .offset-by-nine.column, 449 | .offset-by-nine.columns { margin-left: 78.0%; } 450 | .offset-by-ten.column, 451 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 452 | .offset-by-eleven.column, 453 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 454 | 455 | .offset-by-one-third.column, 456 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 457 | .offset-by-two-thirds.column, 458 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 459 | 460 | .offset-by-one-half.column, 461 | .offset-by-one-half.columns { margin-left: 52%; } 462 | 463 | } 464 | 465 | 466 | /* Base Styles 467 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 468 | /* NOTE 469 | html is set to 62.5% so that all the REM measurements throughout Skeleton 470 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 471 | html { 472 | font-size: 62.5%; } 473 | body { 474 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 475 | line-height: 1.6; 476 | font-weight: 400; 477 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 478 | color: #222; } 479 | 480 | 481 | /* Typography 482 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 483 | h1, h2, h3, h4, h5, h6 { 484 | margin-top: 0; 485 | margin-bottom: 2rem; 486 | font-weight: 300; } 487 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 488 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 489 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 490 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 491 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 492 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 493 | 494 | /* Larger than phablet */ 495 | @media (min-width: 550px) { 496 | h1 { font-size: 5.0rem; } 497 | h2 { font-size: 4.2rem; } 498 | h3 { font-size: 3.6rem; } 499 | h4 { font-size: 3.0rem; } 500 | h5 { font-size: 2.4rem; } 501 | h6 { font-size: 1.5rem; } 502 | } 503 | 504 | p { 505 | margin-top: 0; } 506 | 507 | 508 | /* Links 509 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 510 | a { 511 | color: #1EAEDB; } 512 | a:hover { 513 | color: #0FA0CE; } 514 | 515 | 516 | /* Buttons 517 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 518 | .button, 519 | button, 520 | input[type="submit"], 521 | input[type="reset"], 522 | input[type="button"] { 523 | display: inline-block; 524 | height: 38px; 525 | padding: 0 30px; 526 | color: #555; 527 | text-align: center; 528 | font-size: 11px; 529 | font-weight: 600; 530 | line-height: 38px; 531 | letter-spacing: .1rem; 532 | text-transform: uppercase; 533 | text-decoration: none; 534 | white-space: nowrap; 535 | background-color: transparent; 536 | border-radius: 4px; 537 | border: 1px solid #bbb; 538 | cursor: pointer; 539 | box-sizing: border-box; } 540 | .button:hover, 541 | button:hover, 542 | input[type="submit"]:hover, 543 | input[type="reset"]:hover, 544 | input[type="button"]:hover, 545 | .button:focus, 546 | button:focus, 547 | input[type="submit"]:focus, 548 | input[type="reset"]:focus, 549 | input[type="button"]:focus { 550 | color: #333; 551 | border-color: #888; 552 | outline: 0; } 553 | .button.button-primary, 554 | button.button-primary, 555 | input[type="submit"].button-primary, 556 | input[type="reset"].button-primary, 557 | input[type="button"].button-primary { 558 | color: #FFF; 559 | background-color: #33C3F0; 560 | border-color: #33C3F0; } 561 | .button.button-primary:hover, 562 | button.button-primary:hover, 563 | input[type="submit"].button-primary:hover, 564 | input[type="reset"].button-primary:hover, 565 | input[type="button"].button-primary:hover, 566 | .button.button-primary:focus, 567 | button.button-primary:focus, 568 | input[type="submit"].button-primary:focus, 569 | input[type="reset"].button-primary:focus, 570 | input[type="button"].button-primary:focus { 571 | color: #FFF; 572 | background-color: #1EAEDB; 573 | border-color: #1EAEDB; } 574 | 575 | 576 | /* Forms 577 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 578 | input[type="email"], 579 | input[type="number"], 580 | input[type="search"], 581 | input[type="text"], 582 | input[type="tel"], 583 | input[type="url"], 584 | input[type="password"], 585 | textarea, 586 | select { 587 | height: 38px; 588 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 589 | background-color: #fff; 590 | border: 1px solid #D1D1D1; 591 | border-radius: 4px; 592 | box-shadow: none; 593 | box-sizing: border-box; } 594 | /* Removes awkward default styles on some inputs for iOS */ 595 | input[type="email"], 596 | input[type="number"], 597 | input[type="search"], 598 | input[type="text"], 599 | input[type="tel"], 600 | input[type="url"], 601 | input[type="password"], 602 | textarea { 603 | -webkit-appearance: none; 604 | -moz-appearance: none; 605 | appearance: none; } 606 | textarea { 607 | min-height: 65px; 608 | padding-top: 6px; 609 | padding-bottom: 6px; } 610 | input[type="email"]:focus, 611 | input[type="number"]:focus, 612 | input[type="search"]:focus, 613 | input[type="text"]:focus, 614 | input[type="tel"]:focus, 615 | input[type="url"]:focus, 616 | input[type="password"]:focus, 617 | textarea:focus, 618 | select:focus { 619 | border: 1px solid #33C3F0; 620 | outline: 0; } 621 | label, 622 | legend { 623 | display: block; 624 | margin-bottom: .5rem; 625 | font-weight: 600; } 626 | fieldset { 627 | padding: 0; 628 | border-width: 0; } 629 | input[type="checkbox"], 630 | input[type="radio"] { 631 | display: inline; } 632 | label > .label-body { 633 | display: inline-block; 634 | margin-left: .5rem; 635 | font-weight: normal; } 636 | 637 | 638 | /* Lists 639 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 640 | ul { 641 | list-style: circle inside; } 642 | ol { 643 | list-style: decimal inside; } 644 | ol, ul { 645 | padding-left: 0; 646 | margin-top: 0; } 647 | ul ul, 648 | ul ol, 649 | ol ol, 650 | ol ul { 651 | margin: 1.5rem 0 1.5rem 3rem; 652 | } 653 | li { 654 | margin-bottom: 1rem; } 655 | 656 | 657 | /* Code 658 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 659 | code { 660 | padding: .2rem .5rem; 661 | margin: 0 .2rem; 662 | font-size: 90%; 663 | white-space: nowrap; 664 | border: 1px solid #E1E1E1; 665 | border-radius: 4px; } 666 | pre > code { 667 | display: block; 668 | padding: 1rem 1.5rem; 669 | white-space: pre; } 670 | 671 | 672 | /* Tables 673 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 674 | th, 675 | td { 676 | padding: 12px 15px; 677 | text-align: left; 678 | border-bottom: 1px solid #E1E1E1; } 679 | th:first-child, 680 | td:first-child { 681 | padding-left: 0; } 682 | th:last-child, 683 | td:last-child { 684 | padding-right: 0; } 685 | 686 | 687 | /* Spacing 688 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 689 | button, 690 | .button { 691 | margin-bottom: 1rem; } 692 | input, 693 | textarea, 694 | select, 695 | fieldset { 696 | margin-bottom: 1.5rem; } 697 | pre, 698 | blockquote, 699 | dl, 700 | figure, 701 | table, 702 | p, 703 | ul, 704 | ol, 705 | form { 706 | margin-bottom: 2.5rem; } 707 | 708 | 709 | /* Utilities 710 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 711 | .u-full-width { 712 | width: 100%; 713 | box-sizing: border-box; } 714 | .u-max-full-width { 715 | max-width: 100%; 716 | box-sizing: border-box; } 717 | .u-pull-right { 718 | float: right; } 719 | .u-pull-left { 720 | float: left; } 721 | 722 | 723 | /* Misc 724 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 725 | hr { 726 | margin-top: 3rem; 727 | margin-bottom: 3.5rem; 728 | border-width: 0; 729 | border-top: 1px solid #E1E1E1; } 730 | 731 | 732 | /* Clearing 733 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 734 | 735 | /* Self Clearing Goodness */ 736 | .container:after, 737 | .row:after, 738 | .u-cf { 739 | content: ""; 740 | display: table; 741 | clear: both; } 742 | 743 | 744 | /* Media Queries 745 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 746 | /* 747 | Note: The best way to structure the use of media queries is to create the queries 748 | near the relevant code. For example, if you wanted to change the styles for buttons 749 | on small devices, paste the mobile query code up in the buttons section and style it 750 | there. 751 | */ 752 | 753 | 754 | /* Larger than mobile */ 755 | @media (min-width: 400px) {} 756 | 757 | /* Larger than phablet (also point when grid becomes active) */ 758 | @media (min-width: 550px) {} 759 | 760 | /* Larger than tablet */ 761 | @media (min-width: 750px) {} 762 | 763 | /* Larger than desktop */ 764 | @media (min-width: 1000px) {} 765 | 766 | /* Larger than Desktop HD */ 767 | @media (min-width: 1200px) {} 768 | 769 | 770 | /* CUSTOM */ 771 | 772 | 773 | body { 774 | letter-spacing: .0425em; 775 | -moz-osx-font-smoothing: grayscale; 776 | } 777 | 778 | .navigation { 779 | display: flex; 780 | justify-content: space-between; 781 | align-items: center; 782 | padding: 10px 0px 10px 0px; 783 | } 784 | 785 | .navigation-item { 786 | display: inline-block; 787 | margin-right: 25px; 788 | } 789 | 790 | .navigation-items { 791 | margin-bottom: 0px; 792 | } 793 | 794 | .navigation-img { 795 | height: 40px; 796 | } 797 | 798 | .navbar { 799 | background-color: #ebebeb; 800 | margin-bottom: 2em; 801 | border-bottom: 1px solid #ddd; 802 | } 803 | 804 | .footbar { 805 | background-color: #ebebeb; 806 | border-top: 1px solid #ddd; 807 | } 808 | 809 | .navigation-link { 810 | text-decoration: none; 811 | } 812 | 813 | .flex-column { 814 | display: flex; 815 | flex-direction: column; 816 | } 817 | 818 | .mtop { 819 | margin-top: 1em; 820 | } 821 | 822 | p { 823 | margin-bottom: 1em !important; 824 | } 825 | 826 | section { 827 | border-radius: 2px; 828 | padding: 20px; 829 | box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; 830 | margin-top:1em; 831 | } 832 | 833 | code { 834 | background-color: inherit; 835 | } 836 | -------------------------------------------------------------------------------- /check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ -t 1 ] 6 | then 7 | YELLOW='\033[0;33m' 8 | GREEN='\033[0;32m' 9 | RED='\033[0;31m' 10 | NC='\033[0m' 11 | fi 12 | 13 | yellow() { printf "${YELLOW}%s${NC}" "$*"; } 14 | green() { printf "${GREEN}%s${NC}" "$*"; } 15 | red() { printf "${RED}%s${NC}" "$*"; } 16 | 17 | good() { 18 | echo "$(green "● success:")" "$@" 19 | } 20 | 21 | bad() { 22 | ret=$1 23 | shift 24 | echo "$(red "● failed:")" "$@" 25 | exit "$ret" 26 | } 27 | 28 | try() { 29 | "$@" || bad $? "$@" && good "$@" 30 | } 31 | 32 | cmd_exists() { 33 | type "$1" >/dev/null 2>&1 34 | } 35 | 36 | cmd_exists go || bad 1 "uhhh, where's your go install? what are you even doing here!" 37 | cmd_exists gofmt || bad 1 "so you have go installed, but not gofmt? wtf mate^^" 38 | cmd_exists golangci-lint || bad 1 "cannot find golangci-lint; check PATH or go to https://github.com/golangci/golangci-lint" 39 | cmd_exists gitleaks || bad 1 "cannot find gitleaks 8.8.8 https://github.com/zricethezav/gitleaks" 40 | 41 | try ./unique_code.py 42 | try gitleaks detect -v -c gitleaks.toml 43 | try gitleaks protect -v -c gitleaks.toml 44 | try go build ./... 45 | try go test -count=1 --tags=integration ./... 46 | { 47 | { 48 | opt='shopt -s extglob nullglob' 49 | gofmt='gofmt -s -w -l !(vendor)/ *.go' 50 | notice=" running: ( $opt; $gofmt; )" 51 | prefix=" $(yellow modified)" 52 | trap 'echo "$notice"; $opt; $gofmt | sed -e "s#^#$prefix #g"' EXIT 53 | } 54 | 55 | # comma separate linters (e.g. "gofmt,stylecheck") 56 | additional_linters="gofmt" 57 | try golangci-lint run --enable $additional_linters ./... 58 | trap '' EXIT 59 | } 60 | -------------------------------------------------------------------------------- /common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "regexp" 7 | "runtime" 8 | "time" 9 | ) 10 | 11 | const ( 12 | UniqueCode = "unique_code" 13 | ) 14 | 15 | // MakeTimestampMilli returns current ms timestamp 16 | func MakeTimestampMilli() int64 { 17 | return time.Now().UnixNano() / int64(time.Millisecond) 18 | } 19 | 20 | var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9 ]+`) 21 | 22 | func RemoveNonAlphanumeric(str string) string { 23 | return nonAlphanumericRegex.ReplaceAllString(str, "") 24 | } 25 | 26 | func Str2ptr(i string) *string { 27 | return &i 28 | } 29 | 30 | func Int2prt(i int32) *int32 { 31 | return &i 32 | } 33 | 34 | func Bool2prt(i bool) *bool { 35 | return &i 36 | } 37 | 38 | func RandomCharacters(size int) string { 39 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 40 | 41 | s := make([]rune, size) 42 | for i := range s { 43 | s[i] = letters[rand.Intn(len(letters))] 44 | } 45 | return string(s) 46 | } 47 | 48 | func MemUsage() string { 49 | var m runtime.MemStats 50 | runtime.ReadMemStats(&m) 51 | // For info on each, see: https://golang.org/pkg/runtime/#MemStats 52 | result := fmt.Sprintf("memoryusage::Alloc = %v MB::TotalAlloc = %v MB::Sys = %v MB::tNumGC = %v", bToMb(m.Alloc), bToMb(m.TotalAlloc), bToMb(m.Sys), m.NumGC) 53 | return result 54 | } 55 | 56 | // useful for getting how much ram we are currently using in case we ever want that 57 | func AllocatedMemUsageMb() uint64 { 58 | var m runtime.MemStats 59 | runtime.ReadMemStats(&m) 60 | return bToMb(m.Alloc) 61 | } 62 | 63 | func bToMb(b uint64) uint64 { 64 | return b / 1024 / 1024 65 | } 66 | -------------------------------------------------------------------------------- /common/config.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/pelletier/go-toml" 5 | ) 6 | 7 | type Config struct { 8 | LogLevel string 9 | HttpPort int 10 | } 11 | 12 | const ( 13 | DefaultLogLevel = "info" 14 | DefaultPort = 8080 15 | ) 16 | 17 | func NewConfig() Config { 18 | config, err := toml.LoadFile("config.toml") 19 | 20 | if err != nil { 21 | return Config{ 22 | LogLevel: DefaultLogLevel, 23 | HttpPort: DefaultPort, 24 | } 25 | } 26 | return Config{ 27 | LogLevel: getOrDefaultString(config, "log.level", DefaultLogLevel), 28 | HttpPort: getOrDefaultInt(config, "server.http_port", DefaultPort), 29 | } 30 | } 31 | 32 | func getOrDefaultString(config *toml.Tree, value, def string) string { 33 | switch config.Get(value).(type) { 34 | case string: 35 | return config.Get(value).(string) 36 | } 37 | 38 | return def 39 | } 40 | 41 | func getOrDefaultInt(config *toml.Tree, value string, def int) int { 42 | switch config.Get(value).(type) { 43 | case int: 44 | return config.Get(value).(int) 45 | case int64: 46 | return int(config.Get(value).(int64)) 47 | } 48 | 49 | return def 50 | } 51 | -------------------------------------------------------------------------------- /common/timeago.go: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/justincampbell/timeago 2 | // SPDX-License-Identifier: MIT 3 | 4 | package common 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "math" 10 | "time" 11 | ) 12 | 13 | const ( 14 | minute = 1 15 | hour = minute * 60 16 | day = hour * 24 17 | month = day * 30 18 | year = day * 365 19 | ) 20 | 21 | // FromDuration returns a friendly string representing an approximation of the 22 | // given duration 23 | func FromDuration(d time.Duration) string { 24 | seconds := round(d.Seconds()) 25 | 26 | if seconds < 30 { 27 | return "less than a minute" 28 | } 29 | 30 | if seconds < 90 { 31 | return "1 minute" 32 | } 33 | 34 | minutes := div(seconds, 60) 35 | 36 | if minutes < 45 { 37 | return fmt.Sprintf("%0d minutes", minutes) 38 | } 39 | 40 | hours := div(minutes, 60) 41 | 42 | if minutes < day { 43 | return fmt.Sprintf("about %s", pluralize(hours, "hour")) 44 | } 45 | 46 | if minutes < (42 * hour) { 47 | return "1 day" 48 | } 49 | 50 | days := div(hours, 24) 51 | 52 | if minutes < (30 * day) { 53 | return pluralize(days, "day") 54 | } 55 | 56 | months := div(days, 30) 57 | 58 | if minutes < (45 * day) { 59 | return "about 1 month" 60 | } 61 | 62 | if minutes < (60 * day) { 63 | return "about 2 months" 64 | } 65 | 66 | if minutes < year { 67 | return pluralize(months, "month") 68 | } 69 | 70 | rem := minutes % year 71 | years := minutes / year 72 | 73 | if rem < (3 * month) { 74 | return fmt.Sprintf("about %s", pluralize(years, "year")) 75 | } 76 | if rem < (9 * month) { 77 | return fmt.Sprintf("over %s", pluralize(years, "year")) 78 | } 79 | 80 | years++ 81 | return fmt.Sprintf("almost %s", pluralize(years, "year")) 82 | } 83 | 84 | // FromTime returns a friendly string representing the approximate difference 85 | // from the given time and time.Now() 86 | func FromTime(t time.Time) string { 87 | now := time.Now() 88 | 89 | var d time.Duration 90 | var suffix string 91 | 92 | if t.Before(now) { 93 | d = now.Sub(t) 94 | suffix = "ago" 95 | } else { 96 | d = t.Sub(now) 97 | suffix = "from now" 98 | } 99 | 100 | return fmt.Sprintf("%s %s", FromDuration(d), suffix) 101 | } 102 | 103 | func pluralize(i int, s string) string { 104 | var buf bytes.Buffer 105 | buf.WriteString(fmt.Sprintf("%d %s", i, s)) 106 | if i != 1 { 107 | buf.WriteString("s") 108 | } 109 | return buf.String() 110 | } 111 | 112 | func round(f float64) int { 113 | return int(math.Floor(f + .50)) 114 | } 115 | 116 | func div(numerator int, denominator int) int { 117 | rem := numerator % denominator 118 | result := numerator / denominator 119 | 120 | if rem >= (denominator / 2) { 121 | result++ 122 | } 123 | 124 | return result 125 | } 126 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | [logs] 2 | level = "info" 3 | 4 | [server] 5 | http_port = 8080 -------------------------------------------------------------------------------- /data/data.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "database/sql" 5 | ) 6 | 7 | type DataModel struct { 8 | DB *sql.DB 9 | } 10 | 11 | func (m *DataModel) GetById(id int64) (*Data, error) { 12 | stmt := ` 13 | SELECT id, 14 | name, 15 | value 16 | FROM data 17 | WHERE id = ? 18 | LIMIT 1 19 | ` 20 | 21 | row := m.DB.QueryRow(stmt, id) 22 | data := &Data{} 23 | 24 | err := row.Scan( 25 | &data.Id, 26 | &data.Name, 27 | &data.Value, 28 | ) 29 | 30 | if err == sql.ErrNoRows { 31 | return nil, ErrNoRecord 32 | } else if err != nil { 33 | return nil, err 34 | } 35 | 36 | return data, nil 37 | } 38 | 39 | func (m *DataModel) GetByName(name string) (*Data, error) { 40 | stmt := ` 41 | SELECT id, 42 | name, 43 | value 44 | FROM data 45 | WHERE name = ? 46 | LIMIT 1 47 | ` 48 | 49 | row := m.DB.QueryRow(stmt, name) 50 | data := &Data{} 51 | 52 | err := row.Scan( 53 | &data.Id, 54 | &data.Name, 55 | &data.Value, 56 | ) 57 | 58 | if err == sql.ErrNoRows { 59 | return nil, ErrNoRecord 60 | } else if err != nil { 61 | return nil, err 62 | } 63 | 64 | return data, nil 65 | } 66 | 67 | func (m *DataModel) Update(data Data) (*Data, error) { 68 | stmt := ` 69 | UPDATE data SET 70 | value = ? 71 | WHERE id=? 72 | LIMIT 1 73 | ` 74 | 75 | _, err := m.DB.Exec(stmt, 76 | data.Value, 77 | data.Id) 78 | 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | acc, err := m.GetById(data.Id) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | return acc, nil 89 | } 90 | 91 | func (m *DataModel) Insert(data Data) error { 92 | stmt := ` 93 | insert into data values (null, ?, ?); 94 | ` 95 | 96 | _, err := m.DB.Exec(stmt, 97 | data.Name, 98 | data.Value) 99 | 100 | if err != nil { 101 | return err 102 | } 103 | 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /data/main.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "changeme/common" 5 | "database/sql" 6 | _ "github.com/go-sql-driver/mysql" 7 | ) 8 | 9 | func ConnectDb(config common.Config) (*sql.DB, error) { 10 | // Connect to the database 11 | //db, err := sql.Open("mysql", config.MySQLConnection) 12 | //if err != nil { 13 | // return nil, err 14 | //} 15 | // 16 | //return db, nil 17 | 18 | return nil, nil 19 | } 20 | -------------------------------------------------------------------------------- /data/models.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ErrNoRecord = errors.New("no record found") 8 | 9 | // Models that match the data store would go below 10 | 11 | // Data example model only from MySQL 12 | // mysql> describe data; 13 | // +-------+--------------+------+-----+---------+----------------+ 14 | // | Field | Type | Null | Key | Default | Extra | 15 | // +-------+--------------+------+-----+---------+----------------+ 16 | // | id | int(11) | NO | PRI | NULL | auto_increment | 17 | // | name | varchar(255) | NO | MUL | NULL | | 18 | // | value | text | NO | | NULL | | 19 | // +-------+--------------+------+-----+---------+----------------+ 20 | type Data struct { 21 | Id int64 22 | Name string 23 | Value string 24 | } 25 | -------------------------------------------------------------------------------- /gitleaks.toml: -------------------------------------------------------------------------------- 1 | # This is the default gitleaks configuration file. 2 | # Rules and allowlists are defined within this file. 3 | # Rules instruct gitleaks on what should be considered a secret. 4 | # Allowlists instruct gitleaks on what is allowed, i.e. not a secret. 5 | title = "gitleaks config" 6 | 7 | [allowlist] 8 | description = "global allow lists" 9 | regexes = [ 10 | '''219-09-9999''', 11 | '''078-05-1120''', 12 | '''(9[0-9]{2}|666)-\d{2}-\d{4}''', 13 | ] 14 | paths = [ 15 | '''gitleaks.toml''', 16 | '''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''', 17 | '''(go.mod|go.sum)$''', 18 | '''vendor''', 19 | ] 20 | 21 | [[rules]] 22 | description = "Adobe Client ID (Oauth Web)" 23 | id = "adobe-client-id" 24 | regex = '''(?i)(?:adobe)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 25 | secretGroup = 1 26 | keywords = [ 27 | "adobe", 28 | ] 29 | 30 | [[rules]] 31 | description = "Adobe Client Secret" 32 | id = "adobe-client-secret" 33 | regex = '''(?i)\b((p8e-)(?i)[a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 34 | keywords = [ 35 | "p8e-", 36 | ] 37 | 38 | [[rules]] 39 | description = "Age secret key" 40 | id = "age secret key" 41 | regex = '''AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}''' 42 | keywords = [ 43 | "age-secret-key-1", 44 | ] 45 | 46 | [[rules]] 47 | description = "Algolia API Key" 48 | id = "algolia-api-key" 49 | regex = '''(?i)(?:algolia)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 50 | keywords = [ 51 | "algolia", 52 | ] 53 | 54 | [[rules]] 55 | description = "Alibaba AccessKey ID" 56 | id = "alibaba-access-key-id" 57 | regex = '''(?i)\b((LTAI)(?i)[a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)''' 58 | keywords = [ 59 | "ltai", 60 | ] 61 | 62 | [[rules]] 63 | description = "Alibaba Secret Key" 64 | id = "alibaba-secret-key" 65 | regex = '''(?i)(?:alibaba)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)''' 66 | secretGroup = 1 67 | keywords = [ 68 | "alibaba", 69 | ] 70 | 71 | [[rules]] 72 | description = "Asana Client ID" 73 | id = "asana-client-id" 74 | regex = '''(?i)(?:asana)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 75 | secretGroup = 1 76 | keywords = [ 77 | "asana", 78 | ] 79 | 80 | [[rules]] 81 | description = "Asana Client Secret" 82 | id = "asana-client-secret" 83 | regex = '''(?i)(?:asana)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 84 | secretGroup = 1 85 | keywords = [ 86 | "asana", 87 | ] 88 | 89 | [[rules]] 90 | description = "Atlassian API token" 91 | id = "atlassian-api-token" 92 | regex = '''(?i)(?:atlassian|confluence)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)''' 93 | secretGroup = 1 94 | keywords = [ 95 | "atlassian","confluence", 96 | ] 97 | 98 | [[rules]] 99 | description = "AWS" 100 | id = "aws-access-token" 101 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 102 | keywords = [ 103 | "akia","agpa","aida","aroa","aipa","anpa","anva","asia", 104 | ] 105 | 106 | [[rules]] 107 | description = "BitBucket Client ID" 108 | id = "bitbucket-client-id" 109 | regex = '''(?i)(?:bitbucket)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 110 | secretGroup = 1 111 | keywords = [ 112 | "bitbucket", 113 | ] 114 | 115 | [[rules]] 116 | description = "BitBucket Client Secret" 117 | id = "bitbucket-client-secret" 118 | regex = '''(?i)(?:bitbucket)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 119 | secretGroup = 1 120 | keywords = [ 121 | "bitbucket", 122 | ] 123 | 124 | [[rules]] 125 | description = "Beamer API token" 126 | id = "beamer-api-token" 127 | regex = '''(?i)(?:beamer)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(b_[a-z0-9=_\-]{44})(?:['|\"|\n|\r|\s|\x60]|$)''' 128 | secretGroup = 1 129 | keywords = [ 130 | "beamer", 131 | ] 132 | 133 | [[rules]] 134 | description = "Clojars API token" 135 | id = "clojars-api-token" 136 | regex = '''(?i)(CLOJARS_)[a-z0-9]{60}''' 137 | keywords = [ 138 | "clojars", 139 | ] 140 | 141 | [[rules]] 142 | description = "Contentful delivery API token" 143 | id = "contentful-delivery-api-token" 144 | regex = '''(?i)(?:contentful)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 145 | secretGroup = 1 146 | keywords = [ 147 | "contentful", 148 | ] 149 | 150 | [[rules]] 151 | description = "Databricks API token" 152 | id = "databricks-api-token" 153 | regex = '''(?i)\b(dapi[a-h0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 154 | keywords = [ 155 | "dapi", 156 | ] 157 | 158 | [[rules]] 159 | description = "Discord API key" 160 | id = "discord-api-token" 161 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 162 | secretGroup = 1 163 | keywords = [ 164 | "discord", 165 | ] 166 | 167 | [[rules]] 168 | description = "Discord client ID" 169 | id = "discord-client-id" 170 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{18})(?:['|\"|\n|\r|\s|\x60]|$)''' 171 | secretGroup = 1 172 | keywords = [ 173 | "discord", 174 | ] 175 | 176 | [[rules]] 177 | description = "Discord client secret" 178 | id = "discord-client-secret" 179 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 180 | secretGroup = 1 181 | keywords = [ 182 | "discord", 183 | ] 184 | 185 | [[rules]] 186 | description = "Dropbox API secret" 187 | id = "dropbox-api-token" 188 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{15})(?:['|\"|\n|\r|\s|\x60]|$)''' 189 | secretGroup = 1 190 | keywords = [ 191 | "dropbox", 192 | ] 193 | 194 | [[rules]] 195 | description = "Dropbox long lived API token" 196 | id = "dropbox-long-lived-api-token" 197 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\-_=]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 198 | keywords = [ 199 | "dropbox", 200 | ] 201 | 202 | [[rules]] 203 | description = "Dropbox short lived API token" 204 | id = "dropbox-short-lived-api-token" 205 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(sl\.[a-z0-9\-=_]{135})(?:['|\"|\n|\r|\s|\x60]|$)''' 206 | keywords = [ 207 | "dropbox", 208 | ] 209 | 210 | [[rules]] 211 | description = "Doppler API token" 212 | id = "doppler-api-token" 213 | regex = '''(dp\.pt\.)(?i)[a-z0-9]{43}''' 214 | keywords = [ 215 | "doppler", 216 | ] 217 | 218 | [[rules]] 219 | description = "Duffel API token" 220 | id = "duffel-api-token" 221 | regex = '''duffel_(test|live)_(?i)[a-z0-9_\-=]{43}''' 222 | keywords = [ 223 | "duffel", 224 | ] 225 | 226 | [[rules]] 227 | description = "Dynatrace API token" 228 | id = "dynatrace-api-token" 229 | regex = '''dt0c01\.(?i)[a-z0-9]{24}\.[a-z0-9]{64}''' 230 | keywords = [ 231 | "dynatrace", 232 | ] 233 | 234 | [[rules]] 235 | description = "EasyPost API token" 236 | id = "easypost-api-token" 237 | regex = '''EZAK(?i)[a-z0-9]{54}''' 238 | keywords = [ 239 | "ezak", 240 | ] 241 | 242 | [[rules]] 243 | description = "EasyPost test API token" 244 | id = "easypost-test-api-token" 245 | regex = '''EZTK(?i)[a-z0-9]{54}''' 246 | keywords = [ 247 | "eztk", 248 | ] 249 | 250 | [[rules]] 251 | description = "facebook" 252 | id = "facebook" 253 | regex = '''(?i)(?:facebook)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 254 | secretGroup = 1 255 | keywords = [ 256 | "facebook", 257 | ] 258 | 259 | [[rules]] 260 | description = "Fastly API key" 261 | id = "fastly-api-token" 262 | regex = '''(?i)(?:fastly)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 263 | secretGroup = 1 264 | keywords = [ 265 | "fastly", 266 | ] 267 | 268 | [[rules]] 269 | description = "Finicity Client Secret" 270 | id = "finicity-client-secret" 271 | regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)''' 272 | secretGroup = 1 273 | keywords = [ 274 | "finicity", 275 | ] 276 | 277 | [[rules]] 278 | description = "Finicity API token" 279 | id = "finicity-api-token" 280 | regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 281 | secretGroup = 1 282 | keywords = [ 283 | "finicity", 284 | ] 285 | 286 | [[rules]] 287 | description = "Finicity Public Key" 288 | id = "flutterwave-public-key" 289 | regex = '''FLWPUBK_TEST-(?i)[a-h0-9]{32}-X''' 290 | keywords = [ 291 | "flwpubk_test", 292 | ] 293 | 294 | [[rules]] 295 | description = "Flutterwave Secret Key" 296 | id = "flutterwave-secret-key" 297 | regex = '''FLWSECK_TEST-(?i)[a-h0-9]{32}-X''' 298 | keywords = [ 299 | "flwseck_test", 300 | ] 301 | 302 | [[rules]] 303 | description = "Flutterwave Encryption Key" 304 | id = "flutterwave-encryption-key" 305 | regex = '''FLWSECK_TEST-(?i)[a-h0-9]{12}''' 306 | keywords = [ 307 | "flwseck_test", 308 | ] 309 | 310 | [[rules]] 311 | description = "Frame.io API token" 312 | id = "frameio-api-token" 313 | regex = '''fio-u-(?i)[a-z0-9\-_=]{64}''' 314 | keywords = [ 315 | "fio-u-", 316 | ] 317 | 318 | [[rules]] 319 | description = "GoCardless API token" 320 | id = "gocardless-api-token" 321 | regex = '''(?i)(?:gocardless)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(live_(?i)[a-z0-9\-_=]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 322 | secretGroup = 1 323 | keywords = [ 324 | "live_","gocardless", 325 | ] 326 | 327 | [[rules]] 328 | description = "GitHub Personal Access Token" 329 | id = "github-pat" 330 | regex = '''ghp_[0-9a-zA-Z]{36}''' 331 | keywords = [ 332 | "ghp_", 333 | ] 334 | 335 | [[rules]] 336 | description = "GitHub OAuth Access Token" 337 | id = "github-oauth" 338 | regex = '''gho_[0-9a-zA-Z]{36}''' 339 | keywords = [ 340 | "gho_", 341 | ] 342 | 343 | [[rules]] 344 | description = "GitHub App Token" 345 | id = "github-app-token" 346 | regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}''' 347 | keywords = [ 348 | "ghu_","ghs_", 349 | ] 350 | 351 | [[rules]] 352 | description = "GitHub Refresh Token" 353 | id = "github-refresh-token" 354 | regex = '''ghr_[0-9a-zA-Z]{36}''' 355 | keywords = [ 356 | "ghr_", 357 | ] 358 | 359 | [[rules]] 360 | description = "Gitlab Personal Access Token" 361 | id = "gitlab-pat" 362 | regex = '''glpat-[0-9a-zA-Z\-\_]{20}''' 363 | keywords = [ 364 | "glpat-", 365 | ] 366 | 367 | [[rules]] 368 | description = "HashiCorp Terraform user/org API token" 369 | id = "hashicorp-tf-api-token" 370 | regex = '''(?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9\-_=]{60,70}''' 371 | keywords = [ 372 | "atlasv1", 373 | ] 374 | 375 | [[rules]] 376 | description = "Heroku API Key" 377 | id = "heroku-api-key" 378 | regex = '''(?i)(?:heroku)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 379 | secretGroup = 1 380 | keywords = [ 381 | "heroku", 382 | ] 383 | 384 | [[rules]] 385 | description = "HubSpot API Token" 386 | id = "hubspot-api-key" 387 | regex = '''(?i)(?:hubspot)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 388 | secretGroup = 1 389 | keywords = [ 390 | "hubspot", 391 | ] 392 | 393 | [[rules]] 394 | description = "Intercom API Token" 395 | id = "intercom-api-key" 396 | regex = '''(?i)(?:intercom)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{60})(?:['|\"|\n|\r|\s|\x60]|$)''' 397 | secretGroup = 1 398 | keywords = [ 399 | "intercom", 400 | ] 401 | 402 | [[rules]] 403 | description = "Linear API Token" 404 | id = "linear-api-key" 405 | regex = '''lin_api_(?i)[a-z0-9]{40}''' 406 | keywords = [ 407 | "lin_api_", 408 | ] 409 | 410 | [[rules]] 411 | description = "Linear Client Secret" 412 | id = "linear-client-secret" 413 | regex = '''(?i)(?:linear)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 414 | secretGroup = 1 415 | keywords = [ 416 | "linear", 417 | ] 418 | 419 | [[rules]] 420 | description = "LinkedIn Client ID" 421 | id = "linkedin-client-id" 422 | regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)''' 423 | secretGroup = 1 424 | keywords = [ 425 | "linkedin","linked-in", 426 | ] 427 | 428 | [[rules]] 429 | description = "LinkedIn Client secret" 430 | id = "linkedin-client-secret" 431 | regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 432 | secretGroup = 1 433 | keywords = [ 434 | "linkedin","linked-in", 435 | ] 436 | 437 | [[rules]] 438 | description = "Lob API Key" 439 | id = "lob-api-key" 440 | regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((live|test)_[a-f0-9]{35})(?:['|\"|\n|\r|\s|\x60]|$)''' 441 | secretGroup = 1 442 | keywords = [ 443 | "test_","live_", 444 | ] 445 | 446 | [[rules]] 447 | description = "Lob Publishable API Key" 448 | id = "lob-pub-api-key" 449 | regex = '''(?i)(?:lob)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((test|live)_pub_[a-f0-9]{31})(?:['|\"|\n|\r|\s|\x60]|$)''' 450 | secretGroup = 1 451 | keywords = [ 452 | "test_pub","live_pub","_pub", 453 | ] 454 | 455 | [[rules]] 456 | description = "Mailchimp API key" 457 | id = "mailchimp-api-key" 458 | regex = '''(?i)(?:mailchimp)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32}-us20)(?:['|\"|\n|\r|\s|\x60]|$)''' 459 | secretGroup = 1 460 | keywords = [ 461 | "mailchimp", 462 | ] 463 | 464 | [[rules]] 465 | description = "Mailgun public validation key" 466 | id = "mailgun-pub-key" 467 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pubkey-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 468 | secretGroup = 1 469 | keywords = [ 470 | "mailgun", 471 | ] 472 | 473 | [[rules]] 474 | description = "Mailgun private API token" 475 | id = "mailgun-private-api-token" 476 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(key-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 477 | secretGroup = 1 478 | keywords = [ 479 | "mailgun", 480 | ] 481 | 482 | [[rules]] 483 | description = "Mailgun webhook signing key" 484 | id = "mailgun-signing-key" 485 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:['|\"|\n|\r|\s|\x60]|$)''' 486 | secretGroup = 1 487 | keywords = [ 488 | "mailgun", 489 | ] 490 | 491 | [[rules]] 492 | description = "MapBox API token" 493 | id = "mapbox-api-token" 494 | regex = '''(?i)(?:mapbox)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pk\.[a-z0-9]{60}\.[a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)''' 495 | secretGroup = 1 496 | keywords = [ 497 | "mapbox", 498 | ] 499 | 500 | [[rules]] 501 | description = "MessageBird API token" 502 | id = "messagebird-api-token" 503 | regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)''' 504 | secretGroup = 1 505 | keywords = [ 506 | "messagebird","message-bird","message_bird", 507 | ] 508 | 509 | [[rules]] 510 | description = "MessageBird client ID" 511 | id = "messagebird-client-id" 512 | regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{8}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 513 | secretGroup = 1 514 | keywords = [ 515 | "messagebird","message-bird","message_bird", 516 | ] 517 | 518 | [[rules]] 519 | description = "New Relic user API Key" 520 | id = "new-relic-user-api-key" 521 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRAK-[a-z0-9]{27})(?:['|\"|\n|\r|\s|\x60]|$)''' 522 | secretGroup = 1 523 | keywords = [ 524 | "nrak", 525 | ] 526 | 527 | [[rules]] 528 | description = "New Relic user API ID" 529 | id = "new-relic-user-api-id" 530 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 531 | secretGroup = 1 532 | keywords = [ 533 | "new-relic","newrelic","new_relic", 534 | ] 535 | 536 | [[rules]] 537 | description = "New Relic ingest browser API token" 538 | id = "new-relic-browser-api-token" 539 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRJS-[a-f0-9]{19})(?:['|\"|\n|\r|\s|\x60]|$)''' 540 | secretGroup = 1 541 | keywords = [ 542 | "nrjs-", 543 | ] 544 | 545 | [[rules]] 546 | description = "npm access token" 547 | id = "npm-access-token" 548 | regex = '''(?i)\b(npm_[a-z0-9]{36})(?:['|\"|\n|\r|\s|\x60]|$)''' 549 | secretGroup = 1 550 | keywords = [ 551 | "npm_", 552 | ] 553 | 554 | [[rules]] 555 | description = "PlanetScale password" 556 | id = "planetscale-password" 557 | regex = '''(?i)\b(pscale_pw_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)''' 558 | secretGroup = 1 559 | keywords = [ 560 | "pscale_pw_", 561 | ] 562 | 563 | [[rules]] 564 | description = "PlanetScale API token" 565 | id = "planetscale-api-token" 566 | regex = '''(?i)\b(pscale_tkn_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)''' 567 | secretGroup = 1 568 | keywords = [ 569 | "pscale_tkn_", 570 | ] 571 | 572 | [[rules]] 573 | description = "PlanetScale OAuth token" 574 | id = "planetscale-oauth-token" 575 | regex = '''(?i)\b(pscale_oauth_(?i)[a-z0-9=\-_\.]{32,64})(?:['|\"|\n|\r|\s|\x60]|$)''' 576 | secretGroup = 1 577 | keywords = [ 578 | "pscale_oauth_", 579 | ] 580 | 581 | [[rules]] 582 | description = "Postman API token" 583 | id = "postman-api-token" 584 | regex = '''(?i)\b(PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34})(?:['|\"|\n|\r|\s|\x60]|$)''' 585 | secretGroup = 1 586 | keywords = [ 587 | "pmak-", 588 | ] 589 | 590 | [[rules]] 591 | description = "Private Key" 592 | id = "private-key" 593 | regex = '''(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY-----[\s\S-]*KEY----''' 594 | keywords = [ 595 | "-----begin", 596 | ] 597 | 598 | [[rules]] 599 | description = "Pulumi API token" 600 | id = "pulumi-api-token" 601 | regex = '''(?i)\b(pul-[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 602 | secretGroup = 1 603 | keywords = [ 604 | "pul-", 605 | ] 606 | 607 | [[rules]] 608 | description = "PyPI upload token" 609 | id = "pypi-upload-token" 610 | regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}''' 611 | keywords = [ 612 | "pypi-ageichlwas5vcmc", 613 | ] 614 | 615 | [[rules]] 616 | description = "Rubygem API token" 617 | id = "rubygems-api-token" 618 | regex = '''(?i)\b(rubygems_[a-f0-9]{48})(?:['|\"|\n|\r|\s|\x60]|$)''' 619 | secretGroup = 1 620 | keywords = [ 621 | "rubygems_", 622 | ] 623 | 624 | [[rules]] 625 | description = "SendGrid API token" 626 | id = "sendgrid-api-token" 627 | regex = '''(?i)\b(SG\.(?i)[a-z0-9=_\-\.]{66})(?:['|\"|\n|\r|\s|\x60]|$)''' 628 | secretGroup = 1 629 | keywords = [ 630 | "sg.", 631 | ] 632 | 633 | [[rules]] 634 | description = "Sendinblue API token" 635 | id = "sendinblue-api-token" 636 | regex = '''(?i)\b(xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 637 | secretGroup = 1 638 | keywords = [ 639 | "xkeysib-", 640 | ] 641 | 642 | [[rules]] 643 | description = "Shippo API token" 644 | id = "shippo-api-token" 645 | regex = '''(?i)\b(shippo_(live|test)_[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 646 | secretGroup = 1 647 | keywords = [ 648 | "shippo_", 649 | ] 650 | 651 | [[rules]] 652 | description = "Shopify access token" 653 | id = "shopify-access-token" 654 | regex = '''shpat_[a-fA-F0-9]{32}''' 655 | keywords = [ 656 | "shpat_", 657 | ] 658 | 659 | [[rules]] 660 | description = "Shopify custom access token" 661 | id = "shopify-custom-access-token" 662 | regex = '''shpca_[a-fA-F0-9]{32}''' 663 | keywords = [ 664 | "shpca_", 665 | ] 666 | 667 | [[rules]] 668 | description = "Shopify private app access token" 669 | id = "shopify-private-app-access-token" 670 | regex = '''shppa_[a-fA-F0-9]{32}''' 671 | keywords = [ 672 | "shppa_", 673 | ] 674 | 675 | [[rules]] 676 | description = "Shopify shared secret" 677 | id = "shopify-shared-secret" 678 | regex = '''shpss_[a-fA-F0-9]{32}''' 679 | keywords = [ 680 | "shpss_", 681 | ] 682 | 683 | [[rules]] 684 | description = "Slack token" 685 | id = "slack-access-token" 686 | regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})''' 687 | keywords = [ 688 | "xoxb","xoxa","xoxp","xoxr","xoxs", 689 | ] 690 | 691 | [[rules]] 692 | description = "Slack Webhook" 693 | id = "slack-web-hook" 694 | regex = '''https:\/\/hooks.slack.com\/services\/[A-Za-z0-9+\/]{44,46}''' 695 | keywords = [ 696 | "hooks.slack.com", 697 | ] 698 | 699 | [[rules]] 700 | description = "Stripe" 701 | id = "stripe-access-token" 702 | regex = '''(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}''' 703 | keywords = [ 704 | "sk_test","pk_test","sk_live","pk_live", 705 | ] 706 | 707 | [[rules]] 708 | description = "Twilio API Key" 709 | id = "twilio-api-key" 710 | regex = '''SK[0-9a-fA-F]{32}''' 711 | keywords = [ 712 | "twilio", 713 | ] 714 | 715 | [[rules]] 716 | description = "Twitch API token" 717 | id = "twitch-api-token" 718 | regex = '''(?i)(?:twitch)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)''' 719 | secretGroup = 1 720 | keywords = [ 721 | "twitch", 722 | ] 723 | 724 | [[rules]] 725 | description = "twitter" 726 | id = "twitter" 727 | regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{35,44})(?:['|\"|\n|\r|\s|\x60]|$)''' 728 | secretGroup = 1 729 | keywords = [ 730 | "twitter", 731 | ] 732 | 733 | [[rules]] 734 | description = "Typeform API token" 735 | id = "typeform-api-token" 736 | regex = '''(?i)(?:typeform)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(tfp_[a-z0-9\-_\.=]{59})(?:['|\"|\n|\r|\s|\x60]|$)''' 737 | secretGroup = 1 738 | keywords = [ 739 | "tfp_", 740 | ] 741 | 742 | [[rules]] 743 | description = "Generic API Key" 744 | id = "generic-api-key" 745 | regex = '''(?i)(?:key|api|token|secret|client|passwd|password|auth)(?:[0-9a-z\-_\t .]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-z\-_.=]{10,150})(?:['|\"|\n|\r|\s|\x60]|$)''' 746 | secretGroup = 1 747 | entropy = 3.5 748 | keywords = [ 749 | "key","api","token","secret","client","passwd","password","auth", 750 | ] 751 | [rules.allowlist] 752 | stopwords= [ 753 | "client", 754 | "endpoint", 755 | "vpn", 756 | "_ec2_", 757 | "aws_", 758 | "authorize", 759 | "author", 760 | "define", 761 | "config", 762 | "credential", 763 | "setting", 764 | "sample", 765 | "xxxxxx", 766 | "000000", 767 | "buffer", 768 | "delete", 769 | "aaaaaa", 770 | "fewfwef", 771 | "getenv", 772 | "env_", 773 | "system", 774 | "example", 775 | "ecdsa", 776 | "sha256", 777 | "sha1", 778 | "sha2", 779 | "md5", 780 | "alert", 781 | "wizard", 782 | "target", 783 | "onboard", 784 | "welcome", 785 | "page", 786 | "exploit", 787 | "experiment", 788 | "expire", 789 | "rabbitmq", 790 | "scraper", 791 | "widget", 792 | "music", 793 | "dns_", 794 | "dns-", 795 | "yahoo", 796 | "want", 797 | "json", 798 | "action", 799 | "script", 800 | "fix_", 801 | "fix-", 802 | "develop", 803 | "compas", 804 | "stripe", 805 | "service", 806 | "master", 807 | "metric", 808 | "tech", 809 | "gitignore", 810 | "rich", 811 | "open", 812 | "stack", 813 | "irc_", 814 | "irc-", 815 | "sublime", 816 | "kohana", 817 | "has_", 818 | "has-", 819 | "fabric", 820 | "wordpres", 821 | "role", 822 | "osx_", 823 | "osx-", 824 | "boost", 825 | "addres", 826 | "queue", 827 | "working", 828 | "sandbox", 829 | "internet", 830 | "print", 831 | "vision", 832 | "tracking", 833 | "being", 834 | "generator", 835 | "traffic", 836 | "world", 837 | "pull", 838 | "rust", 839 | "watcher", 840 | "small", 841 | "auth", 842 | "full", 843 | "hash", 844 | "more", 845 | "install", 846 | "auto", 847 | "complete", 848 | "learn", 849 | "paper", 850 | "installer", 851 | "research", 852 | "acces", 853 | "last", 854 | "binding", 855 | "spine", 856 | "into", 857 | "chat", 858 | "algorithm", 859 | "resource", 860 | "uploader", 861 | "video", 862 | "maker", 863 | "next", 864 | "proc", 865 | "lock", 866 | "robot", 867 | "snake", 868 | "patch", 869 | "matrix", 870 | "drill", 871 | "terminal", 872 | "term", 873 | "stuff", 874 | "genetic", 875 | "generic", 876 | "identity", 877 | "audit", 878 | "pattern", 879 | "audio", 880 | "web_", 881 | "web-", 882 | "crud", 883 | "problem", 884 | "statu", 885 | "cms-", 886 | "cms_", 887 | "arch", 888 | "coffee", 889 | "workflow", 890 | "changelog", 891 | "another", 892 | "uiview", 893 | "content", 894 | "kitchen", 895 | "gnu_", 896 | "gnu-", 897 | "gnu.", 898 | "conf", 899 | "couchdb", 900 | "client", 901 | "opencv", 902 | "rendering", 903 | "update", 904 | "concept", 905 | "varnish", 906 | "gui_", 907 | "gui-", 908 | "gui.", 909 | "version", 910 | "shared", 911 | "extra", 912 | "product", 913 | "still", 914 | "not_", 915 | "not-", 916 | "not.", 917 | "drop", 918 | "ring", 919 | "png_", 920 | "png-", 921 | "png.", 922 | "actively", 923 | "import", 924 | "output", 925 | "backup", 926 | "start", 927 | "embedded", 928 | "registry", 929 | "pool", 930 | "semantic", 931 | "instagram", 932 | "bash", 933 | "system", 934 | "ninja", 935 | "drupal", 936 | "jquery", 937 | "polyfill", 938 | "physic", 939 | "league", 940 | "guide", 941 | "pack", 942 | "synopsi", 943 | "sketch", 944 | "injection", 945 | "svg_", 946 | "svg-", 947 | "svg.", 948 | "friendly", 949 | "wave", 950 | "convert", 951 | "manage", 952 | "camera", 953 | "link", 954 | "slide", 955 | "timer", 956 | "wrapper", 957 | "gallery", 958 | "url_", 959 | "url-", 960 | "url.", 961 | "todomvc", 962 | "requirej", 963 | "party", 964 | "http", 965 | "payment", 966 | "async", 967 | "library", 968 | "home", 969 | "coco", 970 | "gaia", 971 | "display", 972 | "universal", 973 | "function", 974 | "metadata", 975 | "hipchat", 976 | "under", 977 | "room", 978 | "config", 979 | "personal", 980 | "realtime", 981 | "resume", 982 | "database", 983 | "testing", 984 | "tiny", 985 | "basic", 986 | "forum", 987 | "meetup", 988 | "yet_", 989 | "yet-", 990 | "yet.", 991 | "cento", 992 | "dead", 993 | "fluentd", 994 | "editor", 995 | "utilitie", 996 | "run_", 997 | "run-", 998 | "run.", 999 | "box_", 1000 | "box-", 1001 | "box.", 1002 | "bot_", 1003 | "bot-", 1004 | "bot.", 1005 | "making", 1006 | "sample", 1007 | "group", 1008 | "monitor", 1009 | "ajax", 1010 | "parallel", 1011 | "cassandra", 1012 | "ultimate", 1013 | "site", 1014 | "get_", 1015 | "get-", 1016 | "get.", 1017 | "gen_", 1018 | "gen-", 1019 | "gen.", 1020 | "gem_", 1021 | "gem-", 1022 | "gem.", 1023 | "extended", 1024 | "image", 1025 | "knife", 1026 | "asset", 1027 | "nested", 1028 | "zero", 1029 | "plugin", 1030 | "bracket", 1031 | "mule", 1032 | "mozilla", 1033 | "number", 1034 | "act_", 1035 | "act-", 1036 | "act.", 1037 | "map_", 1038 | "map-", 1039 | "map.", 1040 | "micro", 1041 | "debug", 1042 | "openshift", 1043 | "chart", 1044 | "expres", 1045 | "backend", 1046 | "task", 1047 | "source", 1048 | "translate", 1049 | "jbos", 1050 | "composer", 1051 | "sqlite", 1052 | "profile", 1053 | "mustache", 1054 | "mqtt", 1055 | "yeoman", 1056 | "have", 1057 | "builder", 1058 | "smart", 1059 | "like", 1060 | "oauth", 1061 | "school", 1062 | "guideline", 1063 | "captcha", 1064 | "filter", 1065 | "bitcoin", 1066 | "bridge", 1067 | "color", 1068 | "toolbox", 1069 | "discovery", 1070 | "new_", 1071 | "new-", 1072 | "new.", 1073 | "dashboard", 1074 | "when", 1075 | "setting", 1076 | "level", 1077 | "post", 1078 | "standard", 1079 | "port", 1080 | "platform", 1081 | "yui_", 1082 | "yui-", 1083 | "yui.", 1084 | "grunt", 1085 | "animation", 1086 | "haskell", 1087 | "icon", 1088 | "latex", 1089 | "cheat", 1090 | "lua_", 1091 | "lua-", 1092 | "lua.", 1093 | "gulp", 1094 | "case", 1095 | "author", 1096 | "without", 1097 | "simulator", 1098 | "wifi", 1099 | "directory", 1100 | "lisp", 1101 | "list", 1102 | "flat", 1103 | "adventure", 1104 | "story", 1105 | "storm", 1106 | "gpu_", 1107 | "gpu-", 1108 | "gpu.", 1109 | "store", 1110 | "caching", 1111 | "attention", 1112 | "solr", 1113 | "logger", 1114 | "demo", 1115 | "shortener", 1116 | "hadoop", 1117 | "finder", 1118 | "phone", 1119 | "pipeline", 1120 | "range", 1121 | "textmate", 1122 | "showcase", 1123 | "app_", 1124 | "app-", 1125 | "app.", 1126 | "idiomatic", 1127 | "edit", 1128 | "our_", 1129 | "our-", 1130 | "our.", 1131 | "out_", 1132 | "out-", 1133 | "out.", 1134 | "sentiment", 1135 | "linked", 1136 | "why_", 1137 | "why-", 1138 | "why.", 1139 | "local", 1140 | "cube", 1141 | "gmail", 1142 | "job_", 1143 | "job-", 1144 | "job.", 1145 | "rpc_", 1146 | "rpc-", 1147 | "rpc.", 1148 | "contest", 1149 | "tcp_", 1150 | "tcp-", 1151 | "tcp.", 1152 | "usage", 1153 | "buildout", 1154 | "weather", 1155 | "transfer", 1156 | "automated", 1157 | "sphinx", 1158 | "issue", 1159 | "sas_", 1160 | "sas-", 1161 | "sas.", 1162 | "parallax", 1163 | "jasmine", 1164 | "addon", 1165 | "machine", 1166 | "solution", 1167 | "dsl_", 1168 | "dsl-", 1169 | "dsl.", 1170 | "episode", 1171 | "menu", 1172 | "theme", 1173 | "best", 1174 | "adapter", 1175 | "debugger", 1176 | "chrome", 1177 | "tutorial", 1178 | "life", 1179 | "step", 1180 | "people", 1181 | "joomla", 1182 | "paypal", 1183 | "developer", 1184 | "solver", 1185 | "team", 1186 | "current", 1187 | "love", 1188 | "visual", 1189 | "date", 1190 | "data", 1191 | "canva", 1192 | "container", 1193 | "future", 1194 | "xml_", 1195 | "xml-", 1196 | "xml.", 1197 | "twig", 1198 | "nagio", 1199 | "spatial", 1200 | "original", 1201 | "sync", 1202 | "archived", 1203 | "refinery", 1204 | "science", 1205 | "mapping", 1206 | "gitlab", 1207 | "play", 1208 | "ext_", 1209 | "ext-", 1210 | "ext.", 1211 | "session", 1212 | "impact", 1213 | "set_", 1214 | "set-", 1215 | "set.", 1216 | "see_", 1217 | "see-", 1218 | "see.", 1219 | "migration", 1220 | "commit", 1221 | "community", 1222 | "shopify", 1223 | "what'", 1224 | "cucumber", 1225 | "statamic", 1226 | "mysql", 1227 | "location", 1228 | "tower", 1229 | "line", 1230 | "code", 1231 | "amqp", 1232 | "hello", 1233 | "send", 1234 | "index", 1235 | "high", 1236 | "notebook", 1237 | "alloy", 1238 | "python", 1239 | "field", 1240 | "document", 1241 | "soap", 1242 | "edition", 1243 | "email", 1244 | "php_", 1245 | "php-", 1246 | "php.", 1247 | "command", 1248 | "transport", 1249 | "official", 1250 | "upload", 1251 | "study", 1252 | "secure", 1253 | "angularj", 1254 | "akka", 1255 | "scalable", 1256 | "package", 1257 | "request", 1258 | "con_", 1259 | "con-", 1260 | "con.", 1261 | "flexible", 1262 | "security", 1263 | "comment", 1264 | "module", 1265 | "flask", 1266 | "graph", 1267 | "flash", 1268 | "apache", 1269 | "change", 1270 | "window", 1271 | "space", 1272 | "lambda", 1273 | "sheet", 1274 | "bookmark", 1275 | "carousel", 1276 | "friend", 1277 | "objective", 1278 | "jekyll", 1279 | "bootstrap", 1280 | "first", 1281 | "article", 1282 | "gwt_", 1283 | "gwt-", 1284 | "gwt.", 1285 | "classic", 1286 | "media", 1287 | "websocket", 1288 | "touch", 1289 | "desktop", 1290 | "real", 1291 | "read", 1292 | "recorder", 1293 | "moved", 1294 | "storage", 1295 | "validator", 1296 | "add-on", 1297 | "pusher", 1298 | "scs_", 1299 | "scs-", 1300 | "scs.", 1301 | "inline", 1302 | "asp_", 1303 | "asp-", 1304 | "asp.", 1305 | "timeline", 1306 | "base", 1307 | "encoding", 1308 | "ffmpeg", 1309 | "kindle", 1310 | "tinymce", 1311 | "pretty", 1312 | "jpa_", 1313 | "jpa-", 1314 | "jpa.", 1315 | "used", 1316 | "user", 1317 | "required", 1318 | "webhook", 1319 | "download", 1320 | "resque", 1321 | "espresso", 1322 | "cloud", 1323 | "mongo", 1324 | "benchmark", 1325 | "pure", 1326 | "cakephp", 1327 | "modx", 1328 | "mode", 1329 | "reactive", 1330 | "fuel", 1331 | "written", 1332 | "flickr", 1333 | "mail", 1334 | "brunch", 1335 | "meteor", 1336 | "dynamic", 1337 | "neo_", 1338 | "neo-", 1339 | "neo.", 1340 | "new_", 1341 | "new-", 1342 | "new.", 1343 | "net_", 1344 | "net-", 1345 | "net.", 1346 | "typo", 1347 | "type", 1348 | "keyboard", 1349 | "erlang", 1350 | "adobe", 1351 | "logging", 1352 | "ckeditor", 1353 | "message", 1354 | "iso_", 1355 | "iso-", 1356 | "iso.", 1357 | "hook", 1358 | "ldap", 1359 | "folder", 1360 | "reference", 1361 | "railscast", 1362 | "www_", 1363 | "www-", 1364 | "www.", 1365 | "tracker", 1366 | "azure", 1367 | "fork", 1368 | "form", 1369 | "digital", 1370 | "exporter", 1371 | "skin", 1372 | "string", 1373 | "template", 1374 | "designer", 1375 | "gollum", 1376 | "fluent", 1377 | "entity", 1378 | "language", 1379 | "alfred", 1380 | "summary", 1381 | "wiki", 1382 | "kernel", 1383 | "calendar", 1384 | "plupload", 1385 | "symfony", 1386 | "foundry", 1387 | "remote", 1388 | "talk", 1389 | "search", 1390 | "dev_", 1391 | "dev-", 1392 | "dev.", 1393 | "del_", 1394 | "del-", 1395 | "del.", 1396 | "token", 1397 | "idea", 1398 | "sencha", 1399 | "selector", 1400 | "interface", 1401 | "create", 1402 | "fun_", 1403 | "fun-", 1404 | "fun.", 1405 | "groovy", 1406 | "query", 1407 | "grail", 1408 | "red_", 1409 | "red-", 1410 | "red.", 1411 | "laravel", 1412 | "monkey", 1413 | "slack", 1414 | "supported", 1415 | "instant", 1416 | "value", 1417 | "center", 1418 | "latest", 1419 | "work", 1420 | "but_", 1421 | "but-", 1422 | "but.", 1423 | "bug_", 1424 | "bug-", 1425 | "bug.", 1426 | "virtual", 1427 | "tweet", 1428 | "statsd", 1429 | "studio", 1430 | "path", 1431 | "real-time", 1432 | "frontend", 1433 | "notifier", 1434 | "coding", 1435 | "tool", 1436 | "firmware", 1437 | "flow", 1438 | "random", 1439 | "mediawiki", 1440 | "bosh", 1441 | "been", 1442 | "beer", 1443 | "lightbox", 1444 | "theory", 1445 | "origin", 1446 | "redmine", 1447 | "hub_", 1448 | "hub-", 1449 | "hub.", 1450 | "require", 1451 | "pro_", 1452 | "pro-", 1453 | "pro.", 1454 | "ant_", 1455 | "ant-", 1456 | "ant.", 1457 | "any_", 1458 | "any-", 1459 | "any.", 1460 | "recipe", 1461 | "closure", 1462 | "mapper", 1463 | "event", 1464 | "todo", 1465 | "model", 1466 | "redi", 1467 | "provider", 1468 | "rvm_", 1469 | "rvm-", 1470 | "rvm.", 1471 | "program", 1472 | "memcached", 1473 | "rail", 1474 | "silex", 1475 | "foreman", 1476 | "activity", 1477 | "license", 1478 | "strategy", 1479 | "batch", 1480 | "streaming", 1481 | "fast", 1482 | "use_", 1483 | "use-", 1484 | "use.", 1485 | "usb_", 1486 | "usb-", 1487 | "usb.", 1488 | "impres", 1489 | "academy", 1490 | "slider", 1491 | "please", 1492 | "layer", 1493 | "cros", 1494 | "now_", 1495 | "now-", 1496 | "now.", 1497 | "miner", 1498 | "extension", 1499 | "own_", 1500 | "own-", 1501 | "own.", 1502 | "app_", 1503 | "app-", 1504 | "app.", 1505 | "debian", 1506 | "symphony", 1507 | "example", 1508 | "feature", 1509 | "serie", 1510 | "tree", 1511 | "project", 1512 | "runner", 1513 | "entry", 1514 | "leetcode", 1515 | "layout", 1516 | "webrtc", 1517 | "logic", 1518 | "login", 1519 | "worker", 1520 | "toolkit", 1521 | "mocha", 1522 | "support", 1523 | "back", 1524 | "inside", 1525 | "device", 1526 | "jenkin", 1527 | "contact", 1528 | "fake", 1529 | "awesome", 1530 | "ocaml", 1531 | "bit_", 1532 | "bit-", 1533 | "bit.", 1534 | "drive", 1535 | "screen", 1536 | "prototype", 1537 | "gist", 1538 | "binary", 1539 | "nosql", 1540 | "rest", 1541 | "overview", 1542 | "dart", 1543 | "dark", 1544 | "emac", 1545 | "mongoid", 1546 | "solarized", 1547 | "homepage", 1548 | "emulator", 1549 | "commander", 1550 | "django", 1551 | "yandex", 1552 | "gradle", 1553 | "xcode", 1554 | "writer", 1555 | "crm_", 1556 | "crm-", 1557 | "crm.", 1558 | "jade", 1559 | "startup", 1560 | "error", 1561 | "using", 1562 | "format", 1563 | "name", 1564 | "spring", 1565 | "parser", 1566 | "scratch", 1567 | "magic", 1568 | "try_", 1569 | "try-", 1570 | "try.", 1571 | "rack", 1572 | "directive", 1573 | "challenge", 1574 | "slim", 1575 | "counter", 1576 | "element", 1577 | "chosen", 1578 | "doc_", 1579 | "doc-", 1580 | "doc.", 1581 | "meta", 1582 | "should", 1583 | "button", 1584 | "packet", 1585 | "stream", 1586 | "hardware", 1587 | "android", 1588 | "infinite", 1589 | "password", 1590 | "software", 1591 | "ghost", 1592 | "xamarin", 1593 | "spec", 1594 | "chef", 1595 | "interview", 1596 | "hubot", 1597 | "mvc_", 1598 | "mvc-", 1599 | "mvc.", 1600 | "exercise", 1601 | "leaflet", 1602 | "launcher", 1603 | "air_", 1604 | "air-", 1605 | "air.", 1606 | "photo", 1607 | "board", 1608 | "boxen", 1609 | "way_", 1610 | "way-", 1611 | "way.", 1612 | "computing", 1613 | "welcome", 1614 | "notepad", 1615 | "portfolio", 1616 | "cat_", 1617 | "cat-", 1618 | "cat.", 1619 | "can_", 1620 | "can-", 1621 | "can.", 1622 | "magento", 1623 | "yaml", 1624 | "domain", 1625 | "card", 1626 | "yii_", 1627 | "yii-", 1628 | "yii.", 1629 | "checker", 1630 | "browser", 1631 | "upgrade", 1632 | "only", 1633 | "progres", 1634 | "aura", 1635 | "ruby_", 1636 | "ruby-", 1637 | "ruby.", 1638 | "polymer", 1639 | "util", 1640 | "lite", 1641 | "hackathon", 1642 | "rule", 1643 | "log_", 1644 | "log-", 1645 | "log.", 1646 | "opengl", 1647 | "stanford", 1648 | "skeleton", 1649 | "history", 1650 | "inspector", 1651 | "help", 1652 | "soon", 1653 | "selenium", 1654 | "lab_", 1655 | "lab-", 1656 | "lab.", 1657 | "scheme", 1658 | "schema", 1659 | "look", 1660 | "ready", 1661 | "leveldb", 1662 | "docker", 1663 | "game", 1664 | "minimal", 1665 | "logstash", 1666 | "messaging", 1667 | "within", 1668 | "heroku", 1669 | "mongodb", 1670 | "kata", 1671 | "suite", 1672 | "picker", 1673 | "win_", 1674 | "win-", 1675 | "win.", 1676 | "wip_", 1677 | "wip-", 1678 | "wip.", 1679 | "panel", 1680 | "started", 1681 | "starter", 1682 | "front-end", 1683 | "detector", 1684 | "deploy", 1685 | "editing", 1686 | "based", 1687 | "admin", 1688 | "capture", 1689 | "spree", 1690 | "page", 1691 | "bundle", 1692 | "goal", 1693 | "rpg_", 1694 | "rpg-", 1695 | "rpg.", 1696 | "setup", 1697 | "side", 1698 | "mean", 1699 | "reader", 1700 | "cookbook", 1701 | "mini", 1702 | "modern", 1703 | "seed", 1704 | "dom_", 1705 | "dom-", 1706 | "dom.", 1707 | "doc_", 1708 | "doc-", 1709 | "doc.", 1710 | "dot_", 1711 | "dot-", 1712 | "dot.", 1713 | "syntax", 1714 | "sugar", 1715 | "loader", 1716 | "website", 1717 | "make", 1718 | "kit_", 1719 | "kit-", 1720 | "kit.", 1721 | "protocol", 1722 | "human", 1723 | "daemon", 1724 | "golang", 1725 | "manager", 1726 | "countdown", 1727 | "connector", 1728 | "swagger", 1729 | "map_", 1730 | "map-", 1731 | "map.", 1732 | "mac_", 1733 | "mac-", 1734 | "mac.", 1735 | "man_", 1736 | "man-", 1737 | "man.", 1738 | "orm_", 1739 | "orm-", 1740 | "orm.", 1741 | "org_", 1742 | "org-", 1743 | "org.", 1744 | "little", 1745 | "zsh_", 1746 | "zsh-", 1747 | "zsh.", 1748 | "shop", 1749 | "show", 1750 | "workshop", 1751 | "money", 1752 | "grid", 1753 | "server", 1754 | "octopres", 1755 | "svn_", 1756 | "svn-", 1757 | "svn.", 1758 | "ember", 1759 | "embed", 1760 | "general", 1761 | "file", 1762 | "important", 1763 | "dropbox", 1764 | "portable", 1765 | "public", 1766 | "docpad", 1767 | "fish", 1768 | "sbt_", 1769 | "sbt-", 1770 | "sbt.", 1771 | "done", 1772 | "para", 1773 | "network", 1774 | "common", 1775 | "readme", 1776 | "popup", 1777 | "simple", 1778 | "purpose", 1779 | "mirror", 1780 | "single", 1781 | "cordova", 1782 | "exchange", 1783 | "object", 1784 | "design", 1785 | "gateway", 1786 | "account", 1787 | "lamp", 1788 | "intellij", 1789 | "math", 1790 | "mit_", 1791 | "mit-", 1792 | "mit.", 1793 | "control", 1794 | "enhanced", 1795 | "emitter", 1796 | "multi", 1797 | "add_", 1798 | "add-", 1799 | "add.", 1800 | "about", 1801 | "socket", 1802 | "preview", 1803 | "vagrant", 1804 | "cli_", 1805 | "cli-", 1806 | "cli.", 1807 | "powerful", 1808 | "top_", 1809 | "top-", 1810 | "top.", 1811 | "radio", 1812 | "watch", 1813 | "fluid", 1814 | "amazon", 1815 | "report", 1816 | "couchbase", 1817 | "automatic", 1818 | "detection", 1819 | "sprite", 1820 | "pyramid", 1821 | "portal", 1822 | "advanced", 1823 | "plu_", 1824 | "plu-", 1825 | "plu.", 1826 | "runtime", 1827 | "git_", 1828 | "git-", 1829 | "git.", 1830 | "uri_", 1831 | "uri-", 1832 | "uri.", 1833 | "haml", 1834 | "node", 1835 | "sql_", 1836 | "sql-", 1837 | "sql.", 1838 | "cool", 1839 | "core", 1840 | "obsolete", 1841 | "handler", 1842 | "iphone", 1843 | "extractor", 1844 | "array", 1845 | "copy", 1846 | "nlp_", 1847 | "nlp-", 1848 | "nlp.", 1849 | "reveal", 1850 | "pop_", 1851 | "pop-", 1852 | "pop.", 1853 | "engine", 1854 | "parse", 1855 | "check", 1856 | "html", 1857 | "nest", 1858 | "all_", 1859 | "all-", 1860 | "all.", 1861 | "chinese", 1862 | "buildpack", 1863 | "what", 1864 | "tag_", 1865 | "tag-", 1866 | "tag.", 1867 | "proxy", 1868 | "style", 1869 | "cookie", 1870 | "feed", 1871 | "restful", 1872 | "compiler", 1873 | "creating", 1874 | "prelude", 1875 | "context", 1876 | "java", 1877 | "rspec", 1878 | "mock", 1879 | "backbone", 1880 | "light", 1881 | "spotify", 1882 | "flex", 1883 | "related", 1884 | "shell", 1885 | "which", 1886 | "clas", 1887 | "webapp", 1888 | "swift", 1889 | "ansible", 1890 | "unity", 1891 | "console", 1892 | "tumblr", 1893 | "export", 1894 | "campfire", 1895 | "conway'", 1896 | "made", 1897 | "riak", 1898 | "hero", 1899 | "here", 1900 | "unix", 1901 | "unit", 1902 | "glas", 1903 | "smtp", 1904 | "how_", 1905 | "how-", 1906 | "how.", 1907 | "hot_", 1908 | "hot-", 1909 | "hot.", 1910 | "debug", 1911 | "release", 1912 | "diff", 1913 | "player", 1914 | "easy", 1915 | "right", 1916 | "old_", 1917 | "old-", 1918 | "old.", 1919 | "animate", 1920 | "time", 1921 | "push", 1922 | "explorer", 1923 | "course", 1924 | "training", 1925 | "nette", 1926 | "router", 1927 | "draft", 1928 | "structure", 1929 | "note", 1930 | "salt", 1931 | "where", 1932 | "spark", 1933 | "trello", 1934 | "power", 1935 | "method", 1936 | "social", 1937 | "via_", 1938 | "via-", 1939 | "via.", 1940 | "vim_", 1941 | "vim-", 1942 | "vim.", 1943 | "select", 1944 | "webkit", 1945 | "github", 1946 | "ftp_", 1947 | "ftp-", 1948 | "ftp.", 1949 | "creator", 1950 | "mongoose", 1951 | "led_", 1952 | "led-", 1953 | "led.", 1954 | "movie", 1955 | "currently", 1956 | "pdf_", 1957 | "pdf-", 1958 | "pdf.", 1959 | "load", 1960 | "markdown", 1961 | "phalcon", 1962 | "input", 1963 | "custom", 1964 | "atom", 1965 | "oracle", 1966 | "phonegap", 1967 | "ubuntu", 1968 | "great", 1969 | "rdf_", 1970 | "rdf-", 1971 | "rdf.", 1972 | "popcorn", 1973 | "firefox", 1974 | "zip_", 1975 | "zip-", 1976 | "zip.", 1977 | "cuda", 1978 | "dotfile", 1979 | "static", 1980 | "openwrt", 1981 | "viewer", 1982 | "powered", 1983 | "graphic", 1984 | "les_", 1985 | "les-", 1986 | "les.", 1987 | "doe_", 1988 | "doe-", 1989 | "doe.", 1990 | "maven", 1991 | "word", 1992 | "eclipse", 1993 | "lab_", 1994 | "lab-", 1995 | "lab.", 1996 | "hacking", 1997 | "steam", 1998 | "analytic", 1999 | "option", 2000 | "abstract", 2001 | "archive", 2002 | "reality", 2003 | "switcher", 2004 | "club", 2005 | "write", 2006 | "kafka", 2007 | "arduino", 2008 | "angular", 2009 | "online", 2010 | "title", 2011 | "don't", 2012 | "contao", 2013 | "notice", 2014 | "analyzer", 2015 | "learning", 2016 | "zend", 2017 | "external", 2018 | "staging", 2019 | "busines", 2020 | "tdd_", 2021 | "tdd-", 2022 | "tdd.", 2023 | "scanner", 2024 | "building", 2025 | "snippet", 2026 | "modular", 2027 | "bower", 2028 | "stm_", 2029 | "stm-", 2030 | "stm.", 2031 | "lib_", 2032 | "lib-", 2033 | "lib.", 2034 | "alpha", 2035 | "mobile", 2036 | "clean", 2037 | "linux", 2038 | "nginx", 2039 | "manifest", 2040 | "some", 2041 | "raspberry", 2042 | "gnome", 2043 | "ide_", 2044 | "ide-", 2045 | "ide.", 2046 | "block", 2047 | "statistic", 2048 | "info", 2049 | "drag", 2050 | "youtube", 2051 | "koan", 2052 | "facebook", 2053 | "paperclip", 2054 | "art_", 2055 | "art-", 2056 | "art.", 2057 | "quality", 2058 | "tab_", 2059 | "tab-", 2060 | "tab.", 2061 | "need", 2062 | "dojo", 2063 | "shield", 2064 | "computer", 2065 | "stat", 2066 | "state", 2067 | "twitter", 2068 | "utility", 2069 | "converter", 2070 | "hosting", 2071 | "devise", 2072 | "liferay", 2073 | "updated", 2074 | "force", 2075 | "tip_", 2076 | "tip-", 2077 | "tip.", 2078 | "behavior", 2079 | "active", 2080 | "call", 2081 | "answer", 2082 | "deck", 2083 | "better", 2084 | "principle", 2085 | "ches", 2086 | "bar_", 2087 | "bar-", 2088 | "bar.", 2089 | "reddit", 2090 | "three", 2091 | "haxe", 2092 | "just", 2093 | "plug-in", 2094 | "agile", 2095 | "manual", 2096 | "tetri", 2097 | "super", 2098 | "beta", 2099 | "parsing", 2100 | "doctrine", 2101 | "minecraft", 2102 | "useful", 2103 | "perl", 2104 | "sharing", 2105 | "agent", 2106 | "switch", 2107 | "view", 2108 | "dash", 2109 | "channel", 2110 | "repo", 2111 | "pebble", 2112 | "profiler", 2113 | "warning", 2114 | "cluster", 2115 | "running", 2116 | "markup", 2117 | "evented", 2118 | "mod_", 2119 | "mod-", 2120 | "mod.", 2121 | "share", 2122 | "csv_", 2123 | "csv-", 2124 | "csv.", 2125 | "response", 2126 | "good", 2127 | "house", 2128 | "connect", 2129 | "built", 2130 | "build", 2131 | "find", 2132 | "ipython", 2133 | "webgl", 2134 | "big_", 2135 | "big-", 2136 | "big.", 2137 | "google", 2138 | "scala", 2139 | "sdl_", 2140 | "sdl-", 2141 | "sdl.", 2142 | "sdk_", 2143 | "sdk-", 2144 | "sdk.", 2145 | "native", 2146 | "day_", 2147 | "day-", 2148 | "day.", 2149 | "puppet", 2150 | "text", 2151 | "routing", 2152 | "helper", 2153 | "linkedin", 2154 | "crawler", 2155 | "host", 2156 | "guard", 2157 | "merchant", 2158 | "poker", 2159 | "over", 2160 | "writing", 2161 | "free", 2162 | "classe", 2163 | "component", 2164 | "craft", 2165 | "nodej", 2166 | "phoenix", 2167 | "longer", 2168 | "quick", 2169 | "lazy", 2170 | "memory", 2171 | "clone", 2172 | "hacker", 2173 | "middleman", 2174 | "factory", 2175 | "motion", 2176 | "multiple", 2177 | "tornado", 2178 | "hack", 2179 | "ssh_", 2180 | "ssh-", 2181 | "ssh.", 2182 | "review", 2183 | "vimrc", 2184 | "driver", 2185 | "driven", 2186 | "blog", 2187 | "particle", 2188 | "table", 2189 | "intro", 2190 | "importer", 2191 | "thrift", 2192 | "xmpp", 2193 | "framework", 2194 | "refresh", 2195 | "react", 2196 | "font", 2197 | "librarie", 2198 | "variou", 2199 | "formatter", 2200 | "analysi", 2201 | "karma", 2202 | "scroll", 2203 | "tut_", 2204 | "tut-", 2205 | "tut.", 2206 | "apple", 2207 | "tag_", 2208 | "tag-", 2209 | "tag.", 2210 | "tab_", 2211 | "tab-", 2212 | "tab.", 2213 | "category", 2214 | "ionic", 2215 | "cache", 2216 | "homebrew", 2217 | "reverse", 2218 | "english", 2219 | "getting", 2220 | "shipping", 2221 | "clojure", 2222 | "boot", 2223 | "book", 2224 | "branch", 2225 | "combination", 2226 | "combo", 2227 | ] -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module changeme 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.7.1 7 | github.com/gorilla/mux v1.8.0 8 | github.com/pelletier/go-toml v1.9.5 9 | github.com/rs/zerolog v1.29.1 10 | ) 11 | 12 | require ( 13 | github.com/mattn/go-colorable v0.1.12 // indirect 14 | github.com/mattn/go-isatty v0.0.14 // indirect 15 | golang.org/x/sys v0.5.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 2 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 3 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 4 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 5 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 6 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 7 | github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= 8 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 9 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 10 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 11 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 12 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 13 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 14 | github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 15 | github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= 16 | github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= 17 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 18 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 19 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 20 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 21 | -------------------------------------------------------------------------------- /handlers/healthcheck.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "changeme/common" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/rs/zerolog/log" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | const ( 13 | jsonContentType = "application/json; charset=utf-8" 14 | jsonIndent = " " 15 | ) 16 | 17 | type Timing struct { 18 | TimeMillis int64 `json:"timeMillis"` 19 | Source string `json:"source"` 20 | } 21 | 22 | type HealthCheckResult struct { 23 | Success bool `json:"success"` 24 | Messages []string `json:"messages"` 25 | Time time.Time `json:"time"` 26 | Timing []Timing `json:"timing"` 27 | Response HealthCheckResponse `json:"response"` 28 | } 29 | 30 | type HealthCheckResponse struct { 31 | IpAddress string `json:"ipAddress"` 32 | MemUsage string `json:"memUsage"` 33 | } 34 | 35 | func (app *Application) HealthCheck(w http.ResponseWriter, r *http.Request) { 36 | log.Info().Str(common.UniqueCode, "b10037c0").Str("ip", GetIP(r)).Msg("HealthCheck") 37 | start := common.MakeTimestampMilli() 38 | 39 | t, _ := json.MarshalIndent(HealthCheckResult{ 40 | Success: true, 41 | Messages: []string{}, 42 | Time: time.Now().UTC(), 43 | Timing: []Timing{ 44 | { 45 | Source: "HealthCheck", 46 | TimeMillis: common.MakeTimestampMilli() - start, 47 | }, 48 | }, 49 | Response: HealthCheckResponse{ 50 | IpAddress: GetIP(r), 51 | MemUsage: common.MemUsage(), 52 | }, 53 | }, "", jsonIndent) 54 | 55 | w.Header().Set("Content-Type", jsonContentType) 56 | _, _ = fmt.Fprint(w, string(t)) 57 | } 58 | -------------------------------------------------------------------------------- /handlers/helper.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // GetIP attempts to use the X-FORWARDED-FOR http header for code behind proxies and load balancers 8 | // (such as on hosts like Heroku) while falling back to the RemoteAddr if the header isn't found. 9 | // NB when behind a LB can return value like so "1.143.90.29, 127.0.0.1" 10 | func GetIP(r *http.Request) string { 11 | forwarded := r.Header.Get("X-FORWARDED-FOR") 12 | if forwarded != "" { 13 | return forwarded 14 | } 15 | return r.RemoteAddr 16 | } 17 | -------------------------------------------------------------------------------- /handlers/index.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "changeme/common" 5 | "github.com/rs/zerolog/log" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func (app *Application) Index(w http.ResponseWriter, r *http.Request) { 11 | log.Info().Str(common.UniqueCode, "93c9683d").Str("ip", GetIP(r)).Msg("Index") 12 | 13 | query := strings.TrimSpace(r.URL.Query().Get("q")) 14 | 15 | err := indexTemplate.Execute(w, templateData{ 16 | SearchTerm: query, 17 | }) 18 | if err != nil { 19 | log.Error().Str(common.UniqueCode, "f6fb63c").Str("ip", GetIP(r)).Err(err).Msg("error executing template") 20 | http.Error(w, "Internal Server Error", http.StatusInternalServerError) 21 | return 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /handlers/route.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "changeme/common" 5 | "changeme/service" 6 | "embed" 7 | "github.com/gorilla/mux" 8 | "github.com/rs/zerolog/log" 9 | "io/fs" 10 | "net/http" 11 | ) 12 | 13 | type Application struct { 14 | Service *service.Service 15 | StaticFiles embed.FS 16 | } 17 | 18 | func NewApplication(service *service.Service, staticFiles embed.FS) (Application, error) { 19 | application := Application{ 20 | Service: service, 21 | StaticFiles: staticFiles, 22 | } 23 | err := application.ParseTemplates() 24 | return application, err 25 | } 26 | 27 | func (app *Application) Routes() *mux.Router { 28 | router := mux.NewRouter().StrictSlash(true) 29 | 30 | // JSON routes 31 | router.HandleFunc("/health-check/", app.HealthCheck).Methods("GET") 32 | 33 | // Regular routes 34 | router.HandleFunc("/", app.Index).Methods("GET") 35 | 36 | // Setup to serve files from the supplied directory 37 | staticFS := fs.FS(app.StaticFiles) 38 | // strip off the location such that we route correctly 39 | staticContent, err := fs.Sub(staticFS, "assets/static") 40 | if err != nil { 41 | log.Fatal().Str(common.UniqueCode, "f8819e2a").Err(err).Msg("error with static content") 42 | } 43 | f := http.FileServer(http.FS(staticContent)) 44 | router.PathPrefix("/static/").Handler(http.StripPrefix("/static", f)) 45 | 46 | return router 47 | } 48 | -------------------------------------------------------------------------------- /handlers/template.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "changeme/assets" 5 | "html/template" 6 | ) 7 | 8 | // Full page templates 9 | var indexTemplate *template.Template 10 | 11 | type templateData struct { 12 | SearchTerm string 13 | } 14 | 15 | func (app *Application) ParseTemplates() error { 16 | t, err := template.ParseFS(assets.Assets, "public/html/index.html", "public/html/navbar.tmpl", "public/html/footer.tmpl") 17 | if err != nil { 18 | return err 19 | } 20 | indexTemplate = t 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "changeme/common" 5 | "changeme/handlers" 6 | "changeme/service" 7 | "embed" 8 | "github.com/rs/zerolog" 9 | "github.com/rs/zerolog/log" 10 | "net/http" 11 | "strconv" 12 | ) 13 | 14 | //go:embed assets/static 15 | var staticFiles embed.FS 16 | 17 | func main() { 18 | config := common.NewConfig() 19 | log.Info().Str(common.UniqueCode, "").Interface("config", config).Msg("config") 20 | 21 | ser := service.NewService(config) 22 | 23 | switch config.LogLevel { 24 | case "error": 25 | zerolog.SetGlobalLevel(zerolog.ErrorLevel) 26 | default: 27 | zerolog.SetGlobalLevel(zerolog.InfoLevel) 28 | } 29 | 30 | app, err := handlers.NewApplication(ser, staticFiles) 31 | if err != nil { 32 | log.Error().Str(common.UniqueCode, "5576633b").Err(err).Msg("error creating application") 33 | return 34 | } 35 | 36 | srv := &http.Server{ 37 | Addr: ":" + strconv.Itoa(config.HttpPort), 38 | Handler: app.Routes(), 39 | } 40 | 41 | log.Log().Str(common.UniqueCode, "3812c7e").Msg("starting server on :" + strconv.Itoa(config.HttpPort)) 42 | err = srv.ListenAndServe() 43 | log.Error().Str(common.UniqueCode, "42aa9c1").Err(err).Msg("exiting server") 44 | } 45 | -------------------------------------------------------------------------------- /service/mapcache.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "sync" 4 | 5 | type MapCache struct { 6 | cache sync.Map 7 | } 8 | 9 | func NewMapCache() *MapCache { 10 | return &MapCache{ 11 | cache: sync.Map{}, 12 | } 13 | } 14 | 15 | func (m *MapCache) Get(cacheKey string) ([]byte, bool) { 16 | v, ok := m.cache.Load(cacheKey) 17 | if !ok { 18 | return nil, false 19 | } 20 | 21 | return v.([]byte), true 22 | } 23 | 24 | func (m *MapCache) Set(cacheKey string, value []byte) { 25 | m.cache.Store(cacheKey, value) 26 | } 27 | -------------------------------------------------------------------------------- /service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "changeme/common" 5 | "sync" 6 | ) 7 | 8 | type Service struct { 9 | BackgroundJobsStarted bool 10 | ServiceMutex sync.Mutex 11 | StartTimeMs int64 12 | GenericCache GenericCache // use interface to allow injection 13 | Config common.Config 14 | } 15 | 16 | type GenericCache interface { 17 | Get(cacheKey string) ([]byte, bool) 18 | Set(cacheKey string, value []byte) 19 | } 20 | 21 | func NewService(config common.Config) *Service { 22 | ser := &Service{ 23 | BackgroundJobsStarted: false, 24 | ServiceMutex: sync.Mutex{}, 25 | StartTimeMs: common.MakeTimestampMilli(), 26 | GenericCache: NewMapCache(), 27 | Config: config, 28 | } 29 | 30 | ser.StartBackground() 31 | return ser 32 | } 33 | 34 | func (ser *Service) StartBackground() { 35 | ser.ServiceMutex.Lock() 36 | defer ser.ServiceMutex.Unlock() 37 | 38 | ser.BackgroundJobsStarted = true 39 | 40 | // trigger background jobs here 41 | } 42 | -------------------------------------------------------------------------------- /unique_code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This program runs through the Go code ensuring that the UniqueCode used for logging is indeed 4 | # unique, and if not printing out the duplicated lines and returning a non zero exit code 5 | 6 | import os 7 | import re 8 | import sys 9 | 10 | found_unique = [] 11 | totally_unique = True 12 | 13 | for root, dirs, files in os.walk(".", topdown=True): 14 | 15 | dirs[:] = [d for d in dirs if d not in ["vendor", ".git", ".idea"]] 16 | files[:] = [x for x in files if ".go" in x] 17 | 18 | for name in files: 19 | with open(os.path.join(root, name), "r", encoding="utf-8") as infile: 20 | cnt = 1 21 | lines = [] 22 | for line in infile: 23 | if "common.UniqueCode" in line: 24 | 25 | unique_key = re.findall('"[a-z0-9]{8}"', line) 26 | if len(unique_key) != 0: 27 | 28 | if unique_key[0] in found_unique: 29 | lines.append("%d %s" % (cnt, line)) 30 | totally_unique = False 31 | else: 32 | found_unique.append(unique_key[0]) 33 | 34 | cnt += 1 35 | 36 | if len(lines) != 0: 37 | print(os.path.join(root, name)) 38 | for l in lines: 39 | print(l.strip()) 40 | 41 | 42 | if totally_unique: 43 | sys.exit(0) 44 | else: 45 | sys.exit(1) 46 | --------------------------------------------------------------------------------