zenbook migration
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/zcli-playground.iml" filepath="$PROJECT_DIR$/.idea/zcli-playground.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
||||||
Generated
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PropertiesComponent">{}</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="typesense" level="application" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/tikinang/cdn
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!-- directly served by the object storage (S3) origin -->
|
||||||
|
<img src='https://storage-prg1.zerops.io/my-bucket/path/to/my-image.png'/>
|
||||||
|
<img src='{{ osGetEnv "storage_cdnUrl" }}/path/to/my-image.png'/>
|
||||||
|
|
||||||
|
<!-- served cached S3 object close to users by the "storage" CDN -->
|
||||||
|
<img src='https://storage.cdn.zerops.app/my-bucket/path/to/my-image.png'/>
|
||||||
|
<img src='{{ osGetEnv "storage_apiUrl" }}/{{ osGetEnv "storage_bucketName" }}/path/to/my-image.png'/>
|
||||||
|
|
||||||
|
<!-- directly served by your app -->
|
||||||
|
<img src='/static/path/to/my-image.png'/>
|
||||||
|
|
||||||
|
<!-- served cached static content close to users by the "url" CDN -->
|
||||||
|
<img src='https://url.cdn.zerops.app/my.domain.com/static/path/to/my-image.png'/>
|
||||||
|
<img src='{{ osGetEnv "zeropsCdnUrl" }}/my.domain.com/static/path/to/my-image.png'/>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Fetch Image Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Image loaded with fetch</h1>
|
||||||
|
|
||||||
|
<div id="image-container"></div>
|
||||||
|
<img height="128" src="https://app.zerops.matejpavlicek.cz/static/gopher-profilovka.png" alt="gopher"/>
|
||||||
|
<img height="128" src="https://nginx-fd5.prg1.zerops.app/static/gopher-profilovka.png" alt="gopher-with-cors"/>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
fetch('https://cz-url.cdn.zerops.app/nginx.zerops.matejpavlicek.cz/static/gopher-profilovka-2.png')
|
||||||
|
.then(response => response.blob())
|
||||||
|
.then(blob => {
|
||||||
|
const objectURL = URL.createObjectURL(blob);
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.height = 128;
|
||||||
|
img.src = objectURL;
|
||||||
|
document.getElementById('image-container').appendChild(img);
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
URL.revokeObjectURL(objectURL);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>CDN TEST</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>TEST</h1>
|
||||||
|
<p>
|
||||||
|
direct static
|
||||||
|
<img height="128" src="static/gopher_profilovka.png" alt="gopher"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
cdn static
|
||||||
|
<img height="128" src="https://static.cdn.zerops.app/nginx.zerops.matejpavlicek.cz/static/gopher_profilovka.png" alt="gopher"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
direct s3
|
||||||
|
<img height="128" src="https://storage-prg1.zerops.io/4gh42-bucket1/gopher_profilovka.png" alt="gopher"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
cdn s3
|
||||||
|
<img height="128" src="https://storage.cdn.zerops.app/4gh42-bucket1/gopher_profilovka.png" alt="gopher"/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
fetch static
|
||||||
|
<div id="image-container"></div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
fetch('https://static.cdn.zerops.app/nginx.zerops.matejpavlicek.cz/static/gopher_profilovka.png')
|
||||||
|
.then(response => response.blob())
|
||||||
|
.then(blob => {
|
||||||
|
const objectURL = URL.createObjectURL(blob);
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.height = 128;
|
||||||
|
img.src = objectURL;
|
||||||
|
img.alt = 'gopher'
|
||||||
|
document.getElementById('image-container').appendChild(img);
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
URL.revokeObjectURL(objectURL);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = fmt.Fprintln(w, "OK")
|
||||||
|
})
|
||||||
|
_ = http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
server_name _;
|
||||||
|
root /var/www;
|
||||||
|
|
||||||
|
location ^~ /.git {
|
||||||
|
deny all;
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' '*' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' '*' always;
|
||||||
|
add_header 'Access-Control-Expose-Headers' '*' always;
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
access_log syslog:server=unix:/dev/log,facility=local1,tag=nginx,severity=info default_short;
|
||||||
|
error_log syslog:server=unix:/dev/log,facility=local1,tag=nginx,severity=error;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 271 KiB |
@@ -0,0 +1 @@
|
|||||||
|
HELLO
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
- hostname: app
|
||||||
|
type: alpine@3.20
|
||||||
|
maxContainers: 1
|
||||||
|
- hostname: static
|
||||||
|
type: static@latest
|
||||||
|
maxContainers: 1
|
||||||
|
- hostname: nginx
|
||||||
|
type: nginx@latest
|
||||||
|
maxContainers: 1
|
||||||
|
startWithoutCode: true
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
zerops:
|
||||||
|
- setup: app
|
||||||
|
build:
|
||||||
|
os: alpine
|
||||||
|
base:
|
||||||
|
- go@1
|
||||||
|
buildCommands:
|
||||||
|
- go build -o app main.go
|
||||||
|
cache: true
|
||||||
|
deployFiles:
|
||||||
|
- app
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: alpine@latest
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
httpSupport: true
|
||||||
|
start: ./app
|
||||||
|
|
||||||
|
- setup: static
|
||||||
|
build:
|
||||||
|
deployFiles:
|
||||||
|
- index.html
|
||||||
|
- static/
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: static@latest
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
httpSupport: true
|
||||||
|
|
||||||
|
- setup: nginx
|
||||||
|
build:
|
||||||
|
deployFiles:
|
||||||
|
- index.html
|
||||||
|
- static/
|
||||||
|
- nginx.conf
|
||||||
|
addToRunPrepare:
|
||||||
|
-
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: nginx@latest
|
||||||
|
prepareCommands:
|
||||||
|
- sudo apk update
|
||||||
|
- sudo apk add vim
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
httpSupport: true
|
||||||
|
siteConfigPath: nginx.conf
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
module local
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require github.com/ClickHouse/clickhouse-go/v2 v2.34.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/ClickHouse/ch-go v0.65.1 // indirect
|
||||||
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
|
github.com/go-faster/city v1.0.1 // indirect
|
||||||
|
github.com/go-faster/errors v0.7.1 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
github.com/paulmach/orb v0.11.1 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||||
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/ClickHouse/ch-go v0.65.1 h1:SLuxmLl5Mjj44/XbINsK2HFvzqup0s6rwKLFH347ZhU=
|
||||||
|
github.com/ClickHouse/ch-go v0.65.1/go.mod h1:bsodgURwmrkvkBe5jw1qnGDgyITsYErfONKAHn05nv4=
|
||||||
|
github.com/ClickHouse/clickhouse-go/v2 v2.34.0 h1:Y4rqkdrRHgExvC4o/NTbLdY5LFQ3LHS77/RNFxFX3Co=
|
||||||
|
github.com/ClickHouse/clickhouse-go/v2 v2.34.0/go.mod h1:yioSINoRLVZkLyDzdMXPLRIqhDvel8iLBlwh6Iefso8=
|
||||||
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
||||||
|
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
||||||
|
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
|
||||||
|
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
|
||||||
|
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
||||||
|
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||||
|
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||||
|
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ClickHouse/clickhouse-go/v2"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var dialCount int
|
||||||
|
conn, err := clickhouse.Open(&clickhouse.Options{
|
||||||
|
Addr: []string{"node-stable-1.db.cluster.zerops:9000"},
|
||||||
|
Auth: clickhouse.Auth{
|
||||||
|
Database: "cluster",
|
||||||
|
Username: "zerops",
|
||||||
|
Password: "uvKq9EnwUnXqKQEYcVTYzhoQ",
|
||||||
|
},
|
||||||
|
DialContext: func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
dialCount++
|
||||||
|
var d net.Dialer
|
||||||
|
return d.DialContext(ctx, "tcp", addr)
|
||||||
|
},
|
||||||
|
Debug: true,
|
||||||
|
Debugf: func(format string, v ...any) {
|
||||||
|
fmt.Printf(format+"\n", v...)
|
||||||
|
},
|
||||||
|
Settings: clickhouse.Settings{
|
||||||
|
"max_execution_time": 60,
|
||||||
|
},
|
||||||
|
Compression: &clickhouse.Compression{
|
||||||
|
Method: clickhouse.CompressionLZ4,
|
||||||
|
},
|
||||||
|
DialTimeout: time.Second * 30,
|
||||||
|
MaxOpenConns: 5,
|
||||||
|
MaxIdleConns: 5,
|
||||||
|
ConnMaxLifetime: time.Duration(10) * time.Minute,
|
||||||
|
ConnOpenStrategy: clickhouse.ConnOpenInOrder,
|
||||||
|
BlockBufferSize: 10,
|
||||||
|
MaxCompressionBuffer: 10240,
|
||||||
|
ClientInfo: clickhouse.ClientInfo{ // optional, please see Client info section in the README.md
|
||||||
|
Products: []struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
}{
|
||||||
|
{Name: "my-app", Version: "0.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = conn.Ping(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
conn2, err := sql.Open("mysql", "zerops:uvKq9EnwUnXqKQEYcVTYzhoQ@tcp(node-stable-1.db.cluster.zerops:9004)/")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = conn2.Ping()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module ddosbot
|
||||||
|
|
||||||
|
go 1.25.3
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Read URLs from environment variable
|
||||||
|
urlsEnv := os.Getenv("TARGET_URLS")
|
||||||
|
if urlsEnv == "" {
|
||||||
|
log.Fatal("TARGET_URLS environment variable is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse comma-delimited URLs
|
||||||
|
urls := strings.Split(urlsEnv, ",")
|
||||||
|
for i := range urls {
|
||||||
|
urls[i] = strings.TrimSpace(urls[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urls) == 0 {
|
||||||
|
log.Fatal("No URLs provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Starting load test on %d URLs\n", len(urls))
|
||||||
|
fmt.Println("URLs:", urls)
|
||||||
|
fmt.Println("Press Ctrl+C to stop\n")
|
||||||
|
|
||||||
|
// Counters
|
||||||
|
var (
|
||||||
|
totalRequests uint64
|
||||||
|
successCount uint64
|
||||||
|
errorCount uint64
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitGroup to track all goroutines
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Start time
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
// Spawn a goroutine for each URL
|
||||||
|
for _, url := range urls {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(targetURL string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: time.Second}
|
||||||
|
for {
|
||||||
|
resp, err := client.Get(targetURL)
|
||||||
|
atomic.AddUint64(&totalRequests, 1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("error: %s => %v\n", targetURL, err)
|
||||||
|
atomic.AddUint64(&errorCount, 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
|
atomic.AddUint64(&successCount, 1)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("bad status: %s => %d\n", targetURL, resp.StatusCode)
|
||||||
|
atomic.AddUint64(&errorCount, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress reporter
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
<-ticker.C
|
||||||
|
elapsed := time.Since(startTime).Seconds()
|
||||||
|
requests := atomic.LoadUint64(&totalRequests)
|
||||||
|
success := atomic.LoadUint64(&successCount)
|
||||||
|
errors := atomic.LoadUint64(&errorCount)
|
||||||
|
fmt.Printf("Elapsed: %.0fs | Requests: %d | Success: %d | Errors: %d | Rate: %.0f req/s\n",
|
||||||
|
elapsed, requests, success, errors, float64(requests)/elapsed)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for all goroutines (runs forever until killed)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
services:
|
||||||
|
- hostname: ddosbot
|
||||||
|
zeropsSetup: ddosbot
|
||||||
|
type: go@1
|
||||||
|
minContainers: 10
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
zerops:
|
||||||
|
- setup: ddosbot
|
||||||
|
build:
|
||||||
|
os: alpine
|
||||||
|
base: go@latest
|
||||||
|
buildCommands:
|
||||||
|
- go build main.go
|
||||||
|
deployFiles:
|
||||||
|
- main
|
||||||
|
cache: true
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: alpine@latest
|
||||||
|
envVariables:
|
||||||
|
TARGET_URLS: https://api-e5-8080.app-tor1.zerops.dev,https://api-e0-8080.app-tor1.zerops.dev,https://api-f2-8080.app-tor1.zerops.dev,https://api-f5-8080.app-tor1.zerops.dev,https://api-da-8080.app-tor1.zerops.dev,https://tor1.saidl.info,https://api-ee-8080.app-tor1.zerops.dev,https://api-ec-8080.app-tor1.zerops.dev,https://api-e1-8080.app-tor1.zerops.dev,https://api-e7-8080.app-tor1.zerops.dev,https://api-e4-8080.app-tor1.zerops.dev,https://api-e3-8080.app-tor1.zerops.dev,https://tor1a.saidl.info,https://api-d9-8080.app-tor1.zerops.dev,https://api-e2-8080.app-tor1.zerops.dev,https://api-e6-8080.app-tor1.zerops.dev,https://api-f0-8080.app-tor1.zerops.dev,https://api-d8-8080.app-tor1.zerops.dev,https://api-e9-8080.app-tor1.zerops.dev,https://api-e8-8080.app-tor1.zerops.dev
|
||||||
|
start: ./main
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
module elasticsearch
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/elastic/elastic-transport-go/v8 v8.6.0 // indirect
|
||||||
|
github.com/elastic/go-elasticsearch/v8 v8.15.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
github.com/elastic/elastic-transport-go/v8 v8.6.0 h1:Y2S/FBjx1LlCv5m6pWAF2kDJAHoSjSRSJCApolgfthA=
|
||||||
|
github.com/elastic/elastic-transport-go/v8 v8.6.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
|
||||||
|
github.com/elastic/go-elasticsearch/v8 v8.15.0 h1:IZyJhe7t7WI3NEFdcHnf6IJXqpRf+8S8QWLtZYYyBYk=
|
||||||
|
github.com/elastic/go-elasticsearch/v8 v8.15.0/go.mod h1:HCON3zj4btpqs2N1jjsAy4a/fiAul+YBP00mBH4xik8=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||||
|
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||||
|
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||||
|
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
elasticsearch8 "github.com/elastic/go-elasticsearch/v8"
|
||||||
|
"github.com/elastic/go-elasticsearch/v8/typedapi/core/search"
|
||||||
|
"github.com/elastic/go-elasticsearch/v8/typedapi/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var hostname string
|
||||||
|
var list, ha bool
|
||||||
|
flag.StringVar(&hostname, "host", "elastic.zerops", "hostname to connect to elastic")
|
||||||
|
flag.BoolVar(&list, "list", false, "whether to list the whole index")
|
||||||
|
flag.BoolVar(&ha, "ha", false, "is the cluster ha?")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
client, err := elasticsearch8.NewTypedClient(elasticsearch8.Config{
|
||||||
|
Addresses: []string{fmt.Sprintf("http://%s:9200", hostname)},
|
||||||
|
// Logger: &elastictransport.TextLogger{Output: os.Stdout, EnableRequestBody: true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexName = "my_index"
|
||||||
|
indicesExistsResponse, err := client.Indices.Exists(indexName).Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if !indicesExistsResponse {
|
||||||
|
indicesCreateResponse, err := client.Indices.Create(indexName).Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("created:", indicesCreateResponse.Index)
|
||||||
|
if !ha {
|
||||||
|
_, err = client.Indices.PutSettings().Indices(indexName).NumberOfReplicas("0").Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("already exists:", indexName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if list {
|
||||||
|
size := 10_000
|
||||||
|
searchResponse, err := client.Search().
|
||||||
|
Index(indexName).
|
||||||
|
Request(
|
||||||
|
&search.Request{
|
||||||
|
Query: &types.Query{
|
||||||
|
MatchAll: types.NewMatchAllQuery(),
|
||||||
|
},
|
||||||
|
Size: &size,
|
||||||
|
},
|
||||||
|
).
|
||||||
|
Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, hit := range searchResponse.Hits.Hits {
|
||||||
|
var data map[string]any
|
||||||
|
_ = json.Unmarshal(hit.Source_, &data)
|
||||||
|
fmt.Printf("==> %s\n", data["message"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
fmt.Println("context canceled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := time.Now().Format(time.TimeOnly)
|
||||||
|
document := map[string]any{
|
||||||
|
"name": "yahoo",
|
||||||
|
"message": message,
|
||||||
|
}
|
||||||
|
indexResponse, err := client.Index(indexName).Request(document).Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s ==>\n", message)
|
||||||
|
|
||||||
|
getResponse, err := client.Get(indexName, indexResponse.Id_).Do(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if !getResponse.Found {
|
||||||
|
fmt.Println("error: document not found")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if getResponse.Id_ != indexResponse.Id_ {
|
||||||
|
fmt.Println("error: different ids")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var data map[string]any
|
||||||
|
_ = json.Unmarshal(getResponse.Source_, &data)
|
||||||
|
fmt.Printf("==> %s\n", data["message"])
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/tikinang/godummy
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = fmt.Fprintln(w, "OK")
|
||||||
|
})
|
||||||
|
_ = http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
project:
|
||||||
|
name: test
|
||||||
|
services:
|
||||||
|
- hostname: app
|
||||||
|
type: alpine@latest
|
||||||
|
minContainers: 1
|
||||||
|
maxContainers: 1
|
||||||
|
buildFromGit: https://github.com/tikinang/godummy
|
||||||
|
enableSubdomainAccess: true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
- hostname: app2
|
||||||
|
type: alpine@latest
|
||||||
|
maxContainers: 1
|
||||||
|
buildFromGit: https://github.com/tikinang/godummy
|
||||||
|
enableSubdomainAccess: true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
zerops:
|
||||||
|
- setup: app
|
||||||
|
build:
|
||||||
|
os: alpine
|
||||||
|
base: go@latest
|
||||||
|
buildCommands:
|
||||||
|
- go build -o app main.go
|
||||||
|
cache: true
|
||||||
|
deployFiles:
|
||||||
|
- app
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: alpine@latest
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
httpSupport: true
|
||||||
|
start: ./app
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/tikinang/test
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
fsize int64
|
||||||
|
count int64
|
||||||
|
path string
|
||||||
|
chunk int64
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Int64Var(&fsize, "fsizemb", 1, "file size mb")
|
||||||
|
flag.Int64Var(&chunk, "readsizekb", 4, "read chunk size kb")
|
||||||
|
flag.Int64Var(&count, "count", 16, "count")
|
||||||
|
flag.StringVar(&path, "path", "/var/www", "path")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
fsize = fsize * 1024 * 1024
|
||||||
|
chunk = chunk * 1024
|
||||||
|
|
||||||
|
fmt.Printf("fsize: %d\n", fsize)
|
||||||
|
fmt.Printf("chunk: %d\n", chunk)
|
||||||
|
|
||||||
|
err := os.MkdirAll(path+"/files", 0755)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
for i := int64(0); i < count; i++ {
|
||||||
|
fname := fmt.Sprintf("%s/files/file_%d", path, i)
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int64) {
|
||||||
|
defer wg.Done()
|
||||||
|
manipulateFile(i, fname)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func manipulateFile(i int64, fname string) {
|
||||||
|
file, err := os.Create(fname)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
in := io.LimitReader(rand.Reader, fsize)
|
||||||
|
wrote, err := io.Copy(file, in)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("[%d]: wrote: %s: %d\n", i, fname, wrote)
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Duration(mathrand.Intn(500)+200) * time.Millisecond)
|
||||||
|
offset := mathrand.Int63n(fsize)
|
||||||
|
size := chunk
|
||||||
|
if offset+size > fsize {
|
||||||
|
size = fsize - offset
|
||||||
|
}
|
||||||
|
seeked, err := file.Seek(offset, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[%d]: seek error: %s\n", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("[%d]: seek offset: %d\n", i, seeked)
|
||||||
|
b := bytes.NewBuffer(make([]byte, 0, size))
|
||||||
|
read, err := io.Copy(b, io.LimitReader(file, size))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[%d]: read error: %s\n", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("[%d]: read: %d\n", i, read)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
zerops:
|
||||||
|
- setup: app
|
||||||
|
build:
|
||||||
|
os: alpine
|
||||||
|
base:
|
||||||
|
- go@1
|
||||||
|
buildCommands:
|
||||||
|
- go build -o cacher main.go
|
||||||
|
cache: true
|
||||||
|
deployFiles:
|
||||||
|
- cacher
|
||||||
|
run:
|
||||||
|
os: ubuntu
|
||||||
|
base: ubuntu@latest
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module hedgedocindex
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require github.com/lib/pq v1.10.9 // indirect
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db, err := sql.Open("postgres", os.Getenv("DATABASE_CONNECTION_STRING"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rows, err := db.Query(`SELECT id, alias, title FROM "Notes" WHERE alias IS NOT NULL AND "permission" IN ('freely', 'editable', 'limited') ORDER BY "createdAt" DESC`)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "text/html")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintln(w, "<html>")
|
||||||
|
_, _ = fmt.Fprintln(w, "<ul>")
|
||||||
|
for rows.Next() {
|
||||||
|
var id, title, alias string
|
||||||
|
err = rows.Scan(&id, &alias, &title)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// fmt.Println(id, alias)
|
||||||
|
|
||||||
|
url := strings.TrimSuffix(os.Getenv("HEDGEDOC_URL"), "/") + "/" + alias
|
||||||
|
_, _ = fmt.Fprintln(w, "<li>")
|
||||||
|
_, _ = fmt.Fprintf(w, `%s (<a href="%s">%s</a>)`, title, url, url)
|
||||||
|
_, _ = fmt.Fprintln(w, "</li>")
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintln(w, "</ul>")
|
||||||
|
_, _ = fmt.Fprintln(w, "</html>")
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Fprintln(os.Stderr, "listening on :8080")
|
||||||
|
err = http.ListenAndServe(":8080", http.DefaultServeMux)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
zerops:
|
||||||
|
- setup: hedgedocindex
|
||||||
|
build:
|
||||||
|
os: alpine
|
||||||
|
base:
|
||||||
|
- go@latest
|
||||||
|
buildCommands:
|
||||||
|
- go build -o app main.go
|
||||||
|
cache: true
|
||||||
|
deployFiles:
|
||||||
|
- app
|
||||||
|
run:
|
||||||
|
os: alpine
|
||||||
|
base: alpine@latest
|
||||||
|
envVariables:
|
||||||
|
HEDGEDOC_URL: https://hedgedoc.zerops.io
|
||||||
|
DATABASE_CONNECTION_STRING: ${docdb_connectionString}/${docdb_dbName}
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
httpSupport: true
|
||||||
|
start: ./app
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
module kafka
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/twmb/franz-go v1.17.1
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.13.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/klauspost/compress v1.17.8 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.8.0 // indirect
|
||||||
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||||
|
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/twmb/franz-go v1.17.1 h1:0LwPsbbJeJ9R91DPUHSEd4su82WJWcTY1Zzbgbg4CeQ=
|
||||||
|
github.com/twmb/franz-go v1.17.1/go.mod h1:NreRdJ2F7dziDY/m6VyspWd6sNxHKXdMZI42UfQ3GXM=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.13.0 h1:bJq4C2ZikUE2jh/wl9MtMTQ/kpmnBgVFh8XMQBEC+60=
|
||||||
|
github.com/twmb/franz-go/pkg/kadm v1.13.0/go.mod h1:VMvpfjz/szpH9WB+vGM+rteTzVv0djyHFimci9qm2C0=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.8.0 h1:lAQB9Z3aMrIP9qF9288XcFf/ccaSxEitNA1CDTEIeTA=
|
||||||
|
github.com/twmb/franz-go/pkg/kmsg v1.8.0/go.mod h1:HzYEb8G3uu5XevZbtU0dVbkphaKTHk0X68N5ka4q6mU=
|
||||||
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/twmb/franz-go/pkg/kadm"
|
||||||
|
"github.com/twmb/franz-go/pkg/kgo"
|
||||||
|
"github.com/twmb/franz-go/pkg/sasl/plain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
var password, hostname string
|
||||||
|
var replication int
|
||||||
|
flag.StringVar(&password, "p", "", "password to connect to kafka")
|
||||||
|
flag.StringVar(&hostname, "h", "kafka", "hostname to connect to kafka")
|
||||||
|
flag.IntVar(&replication, "r", 3, "number of replication")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
seeds := []string{fmt.Sprintf("%s.zerops:9092", hostname)}
|
||||||
|
cl, err := kgo.NewClient(
|
||||||
|
kgo.SeedBrokers(seeds...),
|
||||||
|
kgo.SASL(plain.Auth{
|
||||||
|
User: "zerops",
|
||||||
|
Pass: password,
|
||||||
|
}.AsMechanism()),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer cl.Close()
|
||||||
|
|
||||||
|
adminClient := kadm.NewClient(cl)
|
||||||
|
topic, err := adminClient.ListTopics(ctx, "foo")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// if topic.Error() != nil {
|
||||||
|
// panic(topic.Error())
|
||||||
|
// }
|
||||||
|
|
||||||
|
if !topic.Has("foo") {
|
||||||
|
partitions := int32(6)
|
||||||
|
if replication == 1 {
|
||||||
|
partitions = 1
|
||||||
|
}
|
||||||
|
resp, err := adminClient.CreateTopic(ctx, partitions, int16(replication), nil, "foo")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if resp.Err != nil {
|
||||||
|
panic(resp.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
message := time.Now().Format(time.TimeOnly)
|
||||||
|
fmt.Printf("%s ==>\n", message)
|
||||||
|
// Alternatively, ProduceSync exists to synchronously produce a batch of records.
|
||||||
|
record := &kgo.Record{Topic: "foo", Value: []byte(message)}
|
||||||
|
if err := cl.ProduceSync(ctx, record).FirstErr(); err != nil {
|
||||||
|
fmt.Printf("record had a produce error while synchronously producing: %v\n", err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cl.AddConsumeTopics("foo")
|
||||||
|
for {
|
||||||
|
fetches := cl.PollFetches(ctx)
|
||||||
|
if fetches.IsClientClosed() {
|
||||||
|
fmt.Println("closed ")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errs := fetches.Errors(); len(errs) > 0 {
|
||||||
|
// All errors are retried internally when fetching, but non-retriable errors are
|
||||||
|
// returned from polls so that users can notice and take action.
|
||||||
|
panic(fmt.Sprint(errs))
|
||||||
|
}
|
||||||
|
// We can iterate through a record iterator...
|
||||||
|
iter := fetches.RecordIter()
|
||||||
|
for !iter.Done() {
|
||||||
|
record := iter.Next()
|
||||||
|
fmt.Printf("==> %s\n", string(record.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
module meilisearch
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/meilisearch/meilisearch-go v0.28.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/meilisearch/meilisearch-go v0.28.0 h1:f3XJ66ZM+R8bANAOLqsjvoq/HhQNpVJPYoNt6QgNzME=
|
||||||
|
github.com/meilisearch/meilisearch-go v0.28.0/go.mod h1:Szcc9CaDiKIfjdgdt49jlmDKpEzjD+x+b6Y6heMdlQ0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/meilisearch/meilisearch-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var hostname, masterKey string
|
||||||
|
var list bool
|
||||||
|
flag.StringVar(&hostname, "host", "meili.zerops", "hostname to connect to elastic")
|
||||||
|
flag.StringVar(&masterKey, "key", "nope", "master key")
|
||||||
|
flag.BoolVar(&list, "list", false, "whether to list the whole index")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
client := meilisearch.New(fmt.Sprintf("http://%s:7700", hostname), meilisearch.WithAPIKey(masterKey))
|
||||||
|
|
||||||
|
const indexName = "xxx"
|
||||||
|
|
||||||
|
taskInfo, err := client.CreateIndexWithContext(ctx, &meilisearch.IndexConfig{
|
||||||
|
Uid: indexName,
|
||||||
|
PrimaryKey: "id",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status := taskInfo.Status
|
||||||
|
var taskErr any
|
||||||
|
for status != "succeeded" {
|
||||||
|
if status == "failed" || status == "canceled" {
|
||||||
|
fmt.Println("task failed:", taskErr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
task, err := client.GetTaskWithContext(ctx, taskInfo.TaskUID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
status = task.Status
|
||||||
|
taskErr = task.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
indices, err := client.ListIndexesWithContext(ctx, &meilisearch.IndexesQuery{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, index := range indices.Results {
|
||||||
|
fmt.Println(index.UID, index.PrimaryKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
index := client.Index(indexName)
|
||||||
|
|
||||||
|
if list {
|
||||||
|
resp, err := index.SearchWithContext(ctx, "*", &meilisearch.SearchRequest{Limit: 1024 * 1024})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, hit := range resp.Hits {
|
||||||
|
fmt.Printf("LIST: ==> %s\n", hit.(map[string]any)["message"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
fmt.Println("context canceled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
message := now.Format(time.TimeOnly)
|
||||||
|
document := map[string]any{
|
||||||
|
"id": now.UnixNano(),
|
||||||
|
"name": "meili",
|
||||||
|
"message": message,
|
||||||
|
}
|
||||||
|
taskInfo, err = index.AddDocumentsWithContext(ctx, []map[string]any{document})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status = taskInfo.Status
|
||||||
|
for status != "succeeded" {
|
||||||
|
if status == "failed" || status == "canceled" {
|
||||||
|
fmt.Println("task failed:", taskErr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
task, err := index.GetTaskWithContext(ctx, taskInfo.TaskUID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
status = task.Status
|
||||||
|
taskErr = task.Error
|
||||||
|
}
|
||||||
|
fmt.Printf("==> %s\n", document["message"])
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
module nats
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require github.com/nats-io/nats.go v1.37.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/klauspost/compress v1.17.2 // indirect
|
||||||
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
|
golang.org/x/crypto v0.18.0 // indirect
|
||||||
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
)
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||||
|
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||||
|
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||||
|
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||||
|
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||||
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var testJetStream bool
|
||||||
|
var password, host string
|
||||||
|
flag.StringVar(&password, "p", "", "password")
|
||||||
|
flag.StringVar(&host, "h", "nats", "hostname")
|
||||||
|
flag.BoolVar(&testJetStream, "js", false, "jetstream")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
nc, err := nats.Connect(fmt.Sprintf("nats://zerops:%s@%s:4222", password, host))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer nc.Close()
|
||||||
|
|
||||||
|
js, err := jetstream.New(nc)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{Name: "stream1"})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer, err := stream.CreateOrUpdateConsumer(ctx, jetstream.ConsumerConfig{Name: "consumer1"})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = consumer.Consume(func(msg jetstream.Msg) {
|
||||||
|
fmt.Printf("==> %s\n", string(msg.Data()))
|
||||||
|
_ = msg.Ack()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
message := time.Now().Format(time.TimeOnly)
|
||||||
|
fmt.Printf("%s ==>\n", message)
|
||||||
|
_, err = js.Publish(ctx, "stream1", []byte(message))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
|
||||||
|
|
||||||
|
# Ignore git directory.
|
||||||
|
/.git/
|
||||||
|
/.gitignore
|
||||||
|
|
||||||
|
# Ignore bundler config.
|
||||||
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore all environment files.
|
||||||
|
/.env*
|
||||||
|
|
||||||
|
# Ignore all default key files.
|
||||||
|
/config/master.key
|
||||||
|
/config/credentials/*.key
|
||||||
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
/tmp/*
|
||||||
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
|
||||||
|
# Ignore pidfiles, but keep the directory.
|
||||||
|
/tmp/pids/*
|
||||||
|
!/tmp/pids/.keep
|
||||||
|
|
||||||
|
# Ignore storage (uploaded files in development and any SQLite databases).
|
||||||
|
/storage/*
|
||||||
|
!/storage/.keep
|
||||||
|
/tmp/storage/*
|
||||||
|
!/tmp/storage/.keep
|
||||||
|
|
||||||
|
# Ignore assets.
|
||||||
|
/node_modules/
|
||||||
|
/app/assets/builds/*
|
||||||
|
!/app/assets/builds/.keep
|
||||||
|
/public/assets
|
||||||
|
|
||||||
|
# Ignore CI service files.
|
||||||
|
/.github
|
||||||
|
|
||||||
|
# Ignore Kamal files.
|
||||||
|
/config/deploy*.yml
|
||||||
|
/.kamal
|
||||||
|
|
||||||
|
# Ignore development files
|
||||||
|
/.devcontainer
|
||||||
|
|
||||||
|
# Ignore Docker-related files
|
||||||
|
/.dockerignore
|
||||||
|
/Dockerfile*
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# See https://git-scm.com/docs/gitattributes for more about git attribute files.
|
||||||
|
|
||||||
|
# Mark the database schema as having been generated.
|
||||||
|
db/schema.rb linguist-generated
|
||||||
|
|
||||||
|
# Mark any vendored files as having been vendored.
|
||||||
|
vendor/* linguist-vendored
|
||||||
|
config/credentials/*.yml.enc diff=rails_credentials
|
||||||
|
config/credentials.yml.enc diff=rails_credentials
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: bundler
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
Vendored
+90
@@ -0,0 +1,90 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
scan_ruby:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Scan for common Rails security vulnerabilities using static analysis
|
||||||
|
run: bin/brakeman --no-pager
|
||||||
|
|
||||||
|
scan_js:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Scan for security vulnerabilities in JavaScript dependencies
|
||||||
|
run: bin/importmap audit
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Lint code for consistent style
|
||||||
|
run: bin/rubocop -f github
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# services:
|
||||||
|
# redis:
|
||||||
|
# image: redis
|
||||||
|
# ports:
|
||||||
|
# - 6379:6379
|
||||||
|
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
|
steps:
|
||||||
|
- name: Install packages
|
||||||
|
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config google-chrome-stable
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: .ruby-version
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
# REDIS_URL: redis://localhost:6379/0
|
||||||
|
run: bin/rails db:test:prepare test test:system
|
||||||
|
|
||||||
|
- name: Keep screenshots from failed system tests
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: screenshots
|
||||||
|
path: ${{ github.workspace }}/tmp/screenshots
|
||||||
|
if-no-files-found: ignore
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||||
|
#
|
||||||
|
# Temporary files generated by your text editor or operating system
|
||||||
|
# belong in git's global ignore instead:
|
||||||
|
# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
|
||||||
|
|
||||||
|
# Ignore bundler config.
|
||||||
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore all environment files.
|
||||||
|
/.env*
|
||||||
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
/tmp/*
|
||||||
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
|
||||||
|
# Ignore pidfiles, but keep the directory.
|
||||||
|
/tmp/pids/*
|
||||||
|
!/tmp/pids/
|
||||||
|
!/tmp/pids/.keep
|
||||||
|
|
||||||
|
# Ignore storage (uploaded files in development and any SQLite databases).
|
||||||
|
/storage/*
|
||||||
|
!/storage/.keep
|
||||||
|
/tmp/storage/*
|
||||||
|
!/tmp/storage/
|
||||||
|
!/tmp/storage/.keep
|
||||||
|
|
||||||
|
/public/assets
|
||||||
|
|
||||||
|
# Ignore master key for decrypting credentials and more.
|
||||||
|
/config/master.key
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Docker set up on $KAMAL_HOSTS..."
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..."
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample post-deploy hook
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..."
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample pre-build hook
|
||||||
|
#
|
||||||
|
# Checks:
|
||||||
|
# 1. We have a clean checkout
|
||||||
|
# 2. A remote is configured
|
||||||
|
# 3. The branch has been pushed to the remote
|
||||||
|
# 4. The version we are deploying matches the remote
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "Git checkout is not clean, aborting..." >&2
|
||||||
|
git status --porcelain >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
first_remote=$(git remote)
|
||||||
|
|
||||||
|
if [ -z "$first_remote" ]; then
|
||||||
|
echo "No git remote set, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_branch=$(git branch --show-current)
|
||||||
|
|
||||||
|
if [ -z "$current_branch" ]; then
|
||||||
|
echo "Not on a git branch, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
||||||
|
|
||||||
|
if [ -z "$remote_head" ]; then
|
||||||
|
echo "Branch not pushed to remote, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
|
||||||
|
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-connect check
|
||||||
|
#
|
||||||
|
# Warms DNS before connecting to hosts in parallel
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
hosts = ENV["KAMAL_HOSTS"].split(",")
|
||||||
|
results = nil
|
||||||
|
max = 3
|
||||||
|
|
||||||
|
elapsed = Benchmark.realtime do
|
||||||
|
results = hosts.map do |host|
|
||||||
|
Thread.new do
|
||||||
|
tries = 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
||||||
|
rescue SocketError
|
||||||
|
if tries < max
|
||||||
|
puts "Retrying DNS warmup: #{host}"
|
||||||
|
tries += 1
|
||||||
|
sleep rand
|
||||||
|
retry
|
||||||
|
else
|
||||||
|
puts "DNS warmup failed: #{host}"
|
||||||
|
host
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tries
|
||||||
|
end
|
||||||
|
end.map(&:value)
|
||||||
|
end
|
||||||
|
|
||||||
|
retries = results.sum - hosts.size
|
||||||
|
nopes = results.count { |r| r == max }
|
||||||
|
|
||||||
|
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
||||||
Executable
+109
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-deploy hook
|
||||||
|
#
|
||||||
|
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
||||||
|
#
|
||||||
|
# Fails unless the combined status is "success"
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_COMMAND
|
||||||
|
# KAMAL_SUBCOMMAND
|
||||||
|
# KAMAL_ROLE (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
# Only check the build status for production deployments
|
||||||
|
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
require "bundler/inline"
|
||||||
|
|
||||||
|
# true = install gems so this is fast on repeat invocations
|
||||||
|
gemfile(true, quiet: true) do
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "octokit"
|
||||||
|
gem "faraday-retry"
|
||||||
|
end
|
||||||
|
|
||||||
|
MAX_ATTEMPTS = 72
|
||||||
|
ATTEMPTS_GAP = 10
|
||||||
|
|
||||||
|
def exit_with_error(message)
|
||||||
|
$stderr.puts message
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
class GithubStatusChecks
|
||||||
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
|
||||||
|
@git_sha = `git rev-parse HEAD`.strip
|
||||||
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||||||
|
refresh!
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh!
|
||||||
|
@combined_status = github_client.combined_status(remote_url, git_sha)
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
combined_status[:state]
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_status_url
|
||||||
|
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
||||||
|
first_status && first_status[:target_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_count
|
||||||
|
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_count
|
||||||
|
combined_status[:statuses].count
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_status
|
||||||
|
if total_count > 0
|
||||||
|
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
||||||
|
else
|
||||||
|
"Build not started..."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
$stdout.sync = true
|
||||||
|
|
||||||
|
puts "Checking build status..."
|
||||||
|
attempts = 0
|
||||||
|
checks = GithubStatusChecks.new
|
||||||
|
|
||||||
|
begin
|
||||||
|
loop do
|
||||||
|
case checks.state
|
||||||
|
when "success"
|
||||||
|
puts "Checks passed, see #{checks.first_status_url}"
|
||||||
|
exit 0
|
||||||
|
when "failure"
|
||||||
|
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
||||||
|
when "pending"
|
||||||
|
attempts += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
||||||
|
|
||||||
|
puts checks.current_status
|
||||||
|
sleep(ATTEMPTS_GAP)
|
||||||
|
checks.refresh!
|
||||||
|
end
|
||||||
|
rescue Octokit::NotFound
|
||||||
|
exit_with_error "Build status could not be found"
|
||||||
|
end
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
|
||||||
|
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
|
||||||
|
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
|
||||||
|
|
||||||
|
# Example of extracting secrets from 1password (or another compatible pw manager)
|
||||||
|
# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
|
||||||
|
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
|
||||||
|
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})
|
||||||
|
|
||||||
|
# Use a GITHUB_TOKEN if private repositories are needed for the image
|
||||||
|
# GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
|
||||||
|
|
||||||
|
# Grab the registry password from ENV
|
||||||
|
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
||||||
|
|
||||||
|
# Improve security by using a password manager. Never check config/master.key into git!
|
||||||
|
RAILS_MASTER_KEY=$(cat config/master.key)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Omakase Ruby styling for Rails
|
||||||
|
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
|
||||||
|
|
||||||
|
# Overwrite or add rules to create your own house style
|
||||||
|
#
|
||||||
|
# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
|
||||||
|
# Layout/SpaceInsideArrayLiteralBrackets:
|
||||||
|
# Enabled: false
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ruby-3.4.2
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
# check=error=true
|
||||||
|
|
||||||
|
# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
|
||||||
|
# docker build -t ruby .
|
||||||
|
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name ruby ruby
|
||||||
|
|
||||||
|
# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
|
||||||
|
|
||||||
|
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
|
||||||
|
ARG RUBY_VERSION=3.4.2
|
||||||
|
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
|
||||||
|
|
||||||
|
# Rails app lives here
|
||||||
|
WORKDIR /rails
|
||||||
|
|
||||||
|
# Install base packages
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \
|
||||||
|
rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
||||||
|
|
||||||
|
# Set production environment
|
||||||
|
ENV RAILS_ENV="production" \
|
||||||
|
BUNDLE_DEPLOYMENT="1" \
|
||||||
|
BUNDLE_PATH="/usr/local/bundle" \
|
||||||
|
BUNDLE_WITHOUT="development"
|
||||||
|
|
||||||
|
# Throw-away build stage to reduce size of final image
|
||||||
|
FROM base AS build
|
||||||
|
|
||||||
|
# Install packages needed to build gems
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \
|
||||||
|
rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
||||||
|
|
||||||
|
# Install application gems
|
||||||
|
COPY Gemfile Gemfile.lock ./
|
||||||
|
RUN bundle install && \
|
||||||
|
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
|
||||||
|
bundle exec bootsnap precompile --gemfile
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Precompile bootsnap code for faster boot times
|
||||||
|
RUN bundle exec bootsnap precompile app/ lib/
|
||||||
|
|
||||||
|
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
|
||||||
|
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Final stage for app image
|
||||||
|
FROM base
|
||||||
|
|
||||||
|
# Copy built artifacts: gems, application
|
||||||
|
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
|
||||||
|
COPY --from=build /rails /rails
|
||||||
|
|
||||||
|
# Run and own only the runtime files as a non-root user for security
|
||||||
|
RUN groupadd --system --gid 1000 rails && \
|
||||||
|
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
|
||||||
|
chown -R rails:rails db log storage tmp
|
||||||
|
USER 1000:1000
|
||||||
|
|
||||||
|
# Entrypoint prepares the database.
|
||||||
|
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
|
||||||
|
|
||||||
|
# Start server via Thruster by default, this can be overwritten at runtime
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["./bin/thrust", "./bin/rails", "server"]
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
|
||||||
|
gem "rails", "~> 8.0.2"
|
||||||
|
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
|
||||||
|
gem "propshaft"
|
||||||
|
# Use sqlite3 as the database for Active Record
|
||||||
|
gem "sqlite3", ">= 2.1"
|
||||||
|
# Use the Puma web server [https://github.com/puma/puma]
|
||||||
|
gem "puma", ">= 5.0"
|
||||||
|
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
|
||||||
|
gem "importmap-rails"
|
||||||
|
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
|
||||||
|
gem "turbo-rails"
|
||||||
|
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
|
||||||
|
gem "stimulus-rails"
|
||||||
|
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
|
||||||
|
gem "jbuilder"
|
||||||
|
|
||||||
|
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
|
||||||
|
# gem "bcrypt", "~> 3.1.7"
|
||||||
|
|
||||||
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
|
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||||
|
|
||||||
|
# Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
|
||||||
|
gem "solid_cache"
|
||||||
|
gem "solid_queue"
|
||||||
|
gem "solid_cable"
|
||||||
|
|
||||||
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
|
gem "bootsnap", require: false
|
||||||
|
|
||||||
|
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
|
||||||
|
gem "kamal", require: false
|
||||||
|
|
||||||
|
# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
|
||||||
|
gem "thruster", require: false
|
||||||
|
|
||||||
|
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
|
||||||
|
# gem "image_processing", "~> 1.2"
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
|
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
||||||
|
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
|
||||||
|
|
||||||
|
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
|
||||||
|
gem "brakeman", require: false
|
||||||
|
|
||||||
|
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
|
||||||
|
gem "rubocop-rails-omakase", require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
# Use console on exceptions pages [https://github.com/rails/web-console]
|
||||||
|
gem "web-console"
|
||||||
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
|
||||||
|
gem "capybara"
|
||||||
|
gem "selenium-webdriver"
|
||||||
|
end
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
actioncable (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
websocket-driver (>= 0.6.1)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
actionmailbox (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
activejob (= 8.0.2)
|
||||||
|
activerecord (= 8.0.2)
|
||||||
|
activestorage (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
actionmailer (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
actionview (= 8.0.2)
|
||||||
|
activejob (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
actionpack (8.0.2)
|
||||||
|
actionview (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
rack (>= 2.2.4)
|
||||||
|
rack-session (>= 1.0.1)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
useragent (~> 0.16)
|
||||||
|
actiontext (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
activerecord (= 8.0.2)
|
||||||
|
activestorage (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
globalid (>= 0.6.0)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
actionview (8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
builder (~> 3.1)
|
||||||
|
erubi (~> 1.11)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
activejob (8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
globalid (>= 0.3.6)
|
||||||
|
activemodel (8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
activerecord (8.0.2)
|
||||||
|
activemodel (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
timeout (>= 0.4.0)
|
||||||
|
activestorage (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
activejob (= 8.0.2)
|
||||||
|
activerecord (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
marcel (~> 1.0)
|
||||||
|
activesupport (8.0.2)
|
||||||
|
base64
|
||||||
|
benchmark (>= 0.3)
|
||||||
|
bigdecimal
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||||
|
connection_pool (>= 2.2.5)
|
||||||
|
drb
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
logger (>= 1.4.2)
|
||||||
|
minitest (>= 5.1)
|
||||||
|
securerandom (>= 0.3)
|
||||||
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
|
uri (>= 0.13.1)
|
||||||
|
addressable (2.8.7)
|
||||||
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
|
ast (2.4.3)
|
||||||
|
base64 (0.2.0)
|
||||||
|
bcrypt_pbkdf (1.1.1)
|
||||||
|
benchmark (0.4.0)
|
||||||
|
bigdecimal (3.1.9)
|
||||||
|
bindex (0.8.1)
|
||||||
|
bootsnap (1.18.4)
|
||||||
|
msgpack (~> 1.2)
|
||||||
|
brakeman (7.0.2)
|
||||||
|
racc
|
||||||
|
builder (3.3.0)
|
||||||
|
capybara (3.40.0)
|
||||||
|
addressable
|
||||||
|
matrix
|
||||||
|
mini_mime (>= 0.1.3)
|
||||||
|
nokogiri (~> 1.11)
|
||||||
|
rack (>= 1.6.0)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
|
xpath (~> 3.2)
|
||||||
|
concurrent-ruby (1.3.5)
|
||||||
|
connection_pool (2.5.0)
|
||||||
|
crass (1.0.6)
|
||||||
|
date (3.4.1)
|
||||||
|
debug (1.10.0)
|
||||||
|
irb (~> 1.10)
|
||||||
|
reline (>= 0.3.8)
|
||||||
|
dotenv (3.1.7)
|
||||||
|
drb (2.2.1)
|
||||||
|
ed25519 (1.3.0)
|
||||||
|
erubi (1.13.1)
|
||||||
|
et-orbi (1.2.11)
|
||||||
|
tzinfo
|
||||||
|
fugit (1.11.1)
|
||||||
|
et-orbi (~> 1, >= 1.2.11)
|
||||||
|
raabro (~> 1.4)
|
||||||
|
globalid (1.2.1)
|
||||||
|
activesupport (>= 6.1)
|
||||||
|
i18n (1.14.7)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
importmap-rails (2.1.0)
|
||||||
|
actionpack (>= 6.0.0)
|
||||||
|
activesupport (>= 6.0.0)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
io-console (0.8.0)
|
||||||
|
irb (1.15.2)
|
||||||
|
pp (>= 0.6.0)
|
||||||
|
rdoc (>= 4.0.0)
|
||||||
|
reline (>= 0.4.2)
|
||||||
|
jbuilder (2.13.0)
|
||||||
|
actionview (>= 5.0.0)
|
||||||
|
activesupport (>= 5.0.0)
|
||||||
|
json (2.10.2)
|
||||||
|
kamal (2.5.3)
|
||||||
|
activesupport (>= 7.0)
|
||||||
|
base64 (~> 0.2)
|
||||||
|
bcrypt_pbkdf (~> 1.0)
|
||||||
|
concurrent-ruby (~> 1.2)
|
||||||
|
dotenv (~> 3.1)
|
||||||
|
ed25519 (~> 1.2)
|
||||||
|
net-ssh (~> 7.3)
|
||||||
|
sshkit (>= 1.23.0, < 2.0)
|
||||||
|
thor (~> 1.3)
|
||||||
|
zeitwerk (>= 2.6.18, < 3.0)
|
||||||
|
language_server-protocol (3.17.0.4)
|
||||||
|
lint_roller (1.1.0)
|
||||||
|
logger (1.7.0)
|
||||||
|
loofah (2.24.0)
|
||||||
|
crass (~> 1.0.2)
|
||||||
|
nokogiri (>= 1.12.0)
|
||||||
|
mail (2.8.1)
|
||||||
|
mini_mime (>= 0.1.1)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
|
marcel (1.0.4)
|
||||||
|
matrix (0.4.2)
|
||||||
|
mini_mime (1.1.5)
|
||||||
|
minitest (5.25.5)
|
||||||
|
msgpack (1.8.0)
|
||||||
|
net-imap (0.5.6)
|
||||||
|
date
|
||||||
|
net-protocol
|
||||||
|
net-pop (0.1.2)
|
||||||
|
net-protocol
|
||||||
|
net-protocol (0.2.2)
|
||||||
|
timeout
|
||||||
|
net-scp (4.1.0)
|
||||||
|
net-ssh (>= 2.6.5, < 8.0.0)
|
||||||
|
net-sftp (4.0.0)
|
||||||
|
net-ssh (>= 5.0.0, < 8.0.0)
|
||||||
|
net-smtp (0.5.1)
|
||||||
|
net-protocol
|
||||||
|
net-ssh (7.3.0)
|
||||||
|
nio4r (2.7.4)
|
||||||
|
nokogiri (1.18.7-aarch64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.7-aarch64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.7-arm-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.7-arm-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.7-x86_64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.7-x86_64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
ostruct (0.6.1)
|
||||||
|
parallel (1.26.3)
|
||||||
|
parser (3.3.7.4)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
racc
|
||||||
|
pp (0.6.2)
|
||||||
|
prettyprint
|
||||||
|
prettyprint (0.2.0)
|
||||||
|
prism (1.4.0)
|
||||||
|
propshaft (1.1.0)
|
||||||
|
actionpack (>= 7.0.0)
|
||||||
|
activesupport (>= 7.0.0)
|
||||||
|
rack
|
||||||
|
railties (>= 7.0.0)
|
||||||
|
psych (5.2.3)
|
||||||
|
date
|
||||||
|
stringio
|
||||||
|
public_suffix (6.0.1)
|
||||||
|
puma (6.6.0)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
raabro (1.4.0)
|
||||||
|
racc (1.8.1)
|
||||||
|
rack (3.1.12)
|
||||||
|
rack-session (2.1.0)
|
||||||
|
base64 (>= 0.1.0)
|
||||||
|
rack (>= 3.0.0)
|
||||||
|
rack-test (2.2.0)
|
||||||
|
rack (>= 1.3)
|
||||||
|
rackup (2.2.1)
|
||||||
|
rack (>= 3)
|
||||||
|
rails (8.0.2)
|
||||||
|
actioncable (= 8.0.2)
|
||||||
|
actionmailbox (= 8.0.2)
|
||||||
|
actionmailer (= 8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
actiontext (= 8.0.2)
|
||||||
|
actionview (= 8.0.2)
|
||||||
|
activejob (= 8.0.2)
|
||||||
|
activemodel (= 8.0.2)
|
||||||
|
activerecord (= 8.0.2)
|
||||||
|
activestorage (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
bundler (>= 1.15.0)
|
||||||
|
railties (= 8.0.2)
|
||||||
|
rails-dom-testing (2.2.0)
|
||||||
|
activesupport (>= 5.0.0)
|
||||||
|
minitest
|
||||||
|
nokogiri (>= 1.6)
|
||||||
|
rails-html-sanitizer (1.6.2)
|
||||||
|
loofah (~> 2.21)
|
||||||
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
|
railties (8.0.2)
|
||||||
|
actionpack (= 8.0.2)
|
||||||
|
activesupport (= 8.0.2)
|
||||||
|
irb (~> 1.13)
|
||||||
|
rackup (>= 1.0.0)
|
||||||
|
rake (>= 12.2)
|
||||||
|
thor (~> 1.0, >= 1.2.2)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
rainbow (3.1.1)
|
||||||
|
rake (13.2.1)
|
||||||
|
rdoc (6.13.1)
|
||||||
|
psych (>= 4.0.0)
|
||||||
|
regexp_parser (2.10.0)
|
||||||
|
reline (0.6.1)
|
||||||
|
io-console (~> 0.5)
|
||||||
|
rexml (3.4.1)
|
||||||
|
rubocop (1.75.2)
|
||||||
|
json (~> 2.3)
|
||||||
|
language_server-protocol (~> 3.17.0.2)
|
||||||
|
lint_roller (~> 1.1.0)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.3.0.2)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 2.9.3, < 3.0)
|
||||||
|
rubocop-ast (>= 1.44.0, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 4.0)
|
||||||
|
rubocop-ast (1.44.0)
|
||||||
|
parser (>= 3.3.7.2)
|
||||||
|
prism (~> 1.4)
|
||||||
|
rubocop-performance (1.25.0)
|
||||||
|
lint_roller (~> 1.1)
|
||||||
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
|
rubocop-ast (>= 1.38.0, < 2.0)
|
||||||
|
rubocop-rails (2.31.0)
|
||||||
|
activesupport (>= 4.2.0)
|
||||||
|
lint_roller (~> 1.1)
|
||||||
|
rack (>= 1.1)
|
||||||
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
|
rubocop-ast (>= 1.38.0, < 2.0)
|
||||||
|
rubocop-rails-omakase (1.1.0)
|
||||||
|
rubocop (>= 1.72)
|
||||||
|
rubocop-performance (>= 1.24)
|
||||||
|
rubocop-rails (>= 2.30)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
rubyzip (2.4.1)
|
||||||
|
securerandom (0.4.1)
|
||||||
|
selenium-webdriver (4.31.0)
|
||||||
|
base64 (~> 0.2)
|
||||||
|
logger (~> 1.4)
|
||||||
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
|
websocket (~> 1.0)
|
||||||
|
solid_cable (3.0.7)
|
||||||
|
actioncable (>= 7.2)
|
||||||
|
activejob (>= 7.2)
|
||||||
|
activerecord (>= 7.2)
|
||||||
|
railties (>= 7.2)
|
||||||
|
solid_cache (1.0.7)
|
||||||
|
activejob (>= 7.2)
|
||||||
|
activerecord (>= 7.2)
|
||||||
|
railties (>= 7.2)
|
||||||
|
solid_queue (1.1.4)
|
||||||
|
activejob (>= 7.1)
|
||||||
|
activerecord (>= 7.1)
|
||||||
|
concurrent-ruby (>= 1.3.1)
|
||||||
|
fugit (~> 1.11.0)
|
||||||
|
railties (>= 7.1)
|
||||||
|
thor (~> 1.3.1)
|
||||||
|
sqlite3 (2.6.0-aarch64-linux-gnu)
|
||||||
|
sqlite3 (2.6.0-aarch64-linux-musl)
|
||||||
|
sqlite3 (2.6.0-arm-linux-gnu)
|
||||||
|
sqlite3 (2.6.0-arm-linux-musl)
|
||||||
|
sqlite3 (2.6.0-x86_64-linux-gnu)
|
||||||
|
sqlite3 (2.6.0-x86_64-linux-musl)
|
||||||
|
sshkit (1.24.0)
|
||||||
|
base64
|
||||||
|
logger
|
||||||
|
net-scp (>= 1.1.2)
|
||||||
|
net-sftp (>= 2.1.2)
|
||||||
|
net-ssh (>= 2.8.0)
|
||||||
|
ostruct
|
||||||
|
stimulus-rails (1.3.4)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
stringio (3.1.6)
|
||||||
|
thor (1.3.2)
|
||||||
|
thruster (0.1.12)
|
||||||
|
thruster (0.1.12-aarch64-linux)
|
||||||
|
thruster (0.1.12-x86_64-linux)
|
||||||
|
timeout (0.4.3)
|
||||||
|
turbo-rails (2.0.13)
|
||||||
|
actionpack (>= 7.1.0)
|
||||||
|
railties (>= 7.1.0)
|
||||||
|
tzinfo (2.0.6)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
unicode-display_width (3.1.4)
|
||||||
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||||
|
unicode-emoji (4.0.4)
|
||||||
|
uri (1.0.3)
|
||||||
|
useragent (0.16.11)
|
||||||
|
web-console (4.2.1)
|
||||||
|
actionview (>= 6.0.0)
|
||||||
|
activemodel (>= 6.0.0)
|
||||||
|
bindex (>= 0.4.0)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
websocket (1.2.11)
|
||||||
|
websocket-driver (0.7.7)
|
||||||
|
base64
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.5)
|
||||||
|
xpath (3.2.0)
|
||||||
|
nokogiri (~> 1.8)
|
||||||
|
zeitwerk (2.7.2)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
aarch64-linux
|
||||||
|
aarch64-linux-gnu
|
||||||
|
aarch64-linux-musl
|
||||||
|
arm-linux-gnu
|
||||||
|
arm-linux-musl
|
||||||
|
x86_64-linux
|
||||||
|
x86_64-linux-gnu
|
||||||
|
x86_64-linux-musl
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
bootsnap
|
||||||
|
brakeman
|
||||||
|
capybara
|
||||||
|
debug
|
||||||
|
importmap-rails
|
||||||
|
jbuilder
|
||||||
|
kamal
|
||||||
|
propshaft
|
||||||
|
puma (>= 5.0)
|
||||||
|
rails (~> 8.0.2)
|
||||||
|
rubocop-rails-omakase
|
||||||
|
selenium-webdriver
|
||||||
|
solid_cable
|
||||||
|
solid_cache
|
||||||
|
solid_queue
|
||||||
|
sqlite3 (>= 2.1)
|
||||||
|
stimulus-rails
|
||||||
|
thruster
|
||||||
|
turbo-rails
|
||||||
|
tzinfo-data
|
||||||
|
web-console
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.6.7
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# README
|
||||||
|
|
||||||
|
This README would normally document whatever steps are necessary to get the
|
||||||
|
application up and running.
|
||||||
|
|
||||||
|
Things you may want to cover:
|
||||||
|
|
||||||
|
* Ruby version
|
||||||
|
|
||||||
|
* System dependencies
|
||||||
|
|
||||||
|
* Configuration
|
||||||
|
|
||||||
|
* Database creation
|
||||||
|
|
||||||
|
* Database initialization
|
||||||
|
|
||||||
|
* How to run the test suite
|
||||||
|
|
||||||
|
* Services (job queues, cache servers, search engines, etc.)
|
||||||
|
|
||||||
|
* Deployment instructions
|
||||||
|
|
||||||
|
* ...
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
require_relative "config/application"
|
||||||
|
|
||||||
|
Rails.application.load_tasks
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css.
|
||||||
|
*
|
||||||
|
* With Propshaft, assets are served efficiently without preprocessing steps. You can still include
|
||||||
|
* application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
|
||||||
|
* cascading order, meaning styles declared later in the document or manifest will override earlier ones,
|
||||||
|
* depending on specificity.
|
||||||
|
*
|
||||||
|
* Consider organizing styles into separate files for maintainability.
|
||||||
|
*/
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
|
allow_browser versions: :modern
|
||||||
|
end
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
module ApplicationHelper
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||||
|
import "@hotwired/turbo-rails"
|
||||||
|
import "controllers"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Application } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
const application = Application.start()
|
||||||
|
|
||||||
|
// Configure Stimulus development experience
|
||||||
|
application.debug = false
|
||||||
|
window.Stimulus = application
|
||||||
|
|
||||||
|
export { application }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
connect() {
|
||||||
|
this.element.textContent = "Hello World!"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Import and register all your controllers from the importmap via controllers/**/*_controller
|
||||||
|
import { application } from "controllers/application"
|
||||||
|
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
|
||||||
|
eagerLoadControllersFrom("controllers", application)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
# Automatically retry jobs that encountered a deadlock
|
||||||
|
# retry_on ActiveRecord::Deadlocked
|
||||||
|
|
||||||
|
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||||
|
# discard_on ActiveJob::DeserializationError
|
||||||
|
end
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class ApplicationMailer < ActionMailer::Base
|
||||||
|
default from: "from@example.com"
|
||||||
|
layout "mailer"
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
|
primary_abstract_class
|
||||||
|
end
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= content_for(:title) || "Ruby" %></title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
|
<%= yield :head %>
|
||||||
|
|
||||||
|
<%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
|
||||||
|
<%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
|
||||||
|
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png">
|
||||||
|
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
||||||
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
|
||||||
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
||||||
|
<%= javascript_importmap_tags %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style>
|
||||||
|
/* Email styles need to be inline */
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<%= yield %>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "Ruby",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"description": "Ruby.",
|
||||||
|
"theme_color": "red",
|
||||||
|
"background_color": "red"
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Add a service worker for processing Web Push notifications:
|
||||||
|
//
|
||||||
|
// self.addEventListener("push", async (event) => {
|
||||||
|
// const { title, options } = await event.data.json()
|
||||||
|
// event.waitUntil(self.registration.showNotification(title, options))
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// self.addEventListener("notificationclick", function(event) {
|
||||||
|
// event.notification.close()
|
||||||
|
// event.waitUntil(
|
||||||
|
// clients.matchAll({ type: "window" }).then((clientList) => {
|
||||||
|
// for (let i = 0; i < clientList.length; i++) {
|
||||||
|
// let client = clientList[i]
|
||||||
|
// let clientPath = (new URL(client.url)).pathname
|
||||||
|
//
|
||||||
|
// if (clientPath == event.notification.data.path && "focus" in client) {
|
||||||
|
// return client.focus()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (clients.openWindow) {
|
||||||
|
// return clients.openWindow(event.notification.data.path)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// })
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
require "rubygems"
|
||||||
|
require "bundler/setup"
|
||||||
|
|
||||||
|
ARGV.unshift("--ensure-latest")
|
||||||
|
|
||||||
|
load Gem.bin_path("brakeman", "brakeman")
|
||||||
Executable
+109
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'bundle' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require "rubygems"
|
||||||
|
|
||||||
|
m = Module.new do
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def invoked_as_script?
|
||||||
|
File.expand_path($0) == File.expand_path(__FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def env_var_version
|
||||||
|
ENV["BUNDLER_VERSION"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def cli_arg_version
|
||||||
|
return unless invoked_as_script? # don't want to hijack other binstubs
|
||||||
|
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
||||||
|
bundler_version = nil
|
||||||
|
update_index = nil
|
||||||
|
ARGV.each_with_index do |a, i|
|
||||||
|
if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
|
||||||
|
bundler_version = a
|
||||||
|
end
|
||||||
|
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
||||||
|
bundler_version = $1
|
||||||
|
update_index = i
|
||||||
|
end
|
||||||
|
bundler_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def gemfile
|
||||||
|
gemfile = ENV["BUNDLE_GEMFILE"]
|
||||||
|
return gemfile if gemfile && !gemfile.empty?
|
||||||
|
|
||||||
|
File.expand_path("../Gemfile", __dir__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def lockfile
|
||||||
|
lockfile =
|
||||||
|
case File.basename(gemfile)
|
||||||
|
when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
|
||||||
|
else "#{gemfile}.lock"
|
||||||
|
end
|
||||||
|
File.expand_path(lockfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
def lockfile_version
|
||||||
|
return unless File.file?(lockfile)
|
||||||
|
lockfile_contents = File.read(lockfile)
|
||||||
|
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
||||||
|
Regexp.last_match(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bundler_requirement
|
||||||
|
@bundler_requirement ||=
|
||||||
|
env_var_version ||
|
||||||
|
cli_arg_version ||
|
||||||
|
bundler_requirement_for(lockfile_version)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bundler_requirement_for(version)
|
||||||
|
return "#{Gem::Requirement.default}.a" unless version
|
||||||
|
|
||||||
|
bundler_gem_version = Gem::Version.new(version)
|
||||||
|
|
||||||
|
bundler_gem_version.approximate_recommendation
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_bundler!
|
||||||
|
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
||||||
|
|
||||||
|
activate_bundler
|
||||||
|
end
|
||||||
|
|
||||||
|
def activate_bundler
|
||||||
|
gem_error = activation_error_handling do
|
||||||
|
gem "bundler", bundler_requirement
|
||||||
|
end
|
||||||
|
return if gem_error.nil?
|
||||||
|
require_error = activation_error_handling do
|
||||||
|
require "bundler/version"
|
||||||
|
end
|
||||||
|
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
||||||
|
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
||||||
|
exit 42
|
||||||
|
end
|
||||||
|
|
||||||
|
def activation_error_handling
|
||||||
|
yield
|
||||||
|
nil
|
||||||
|
rescue StandardError, LoadError => e
|
||||||
|
e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
m.load_bundler!
|
||||||
|
|
||||||
|
if m.invoked_as_script?
|
||||||
|
load Gem.bin_path("bundler", "bundle")
|
||||||
|
end
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
exec "./bin/rails", "server", *ARGV
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# Enable jemalloc for reduced memory usage and latency.
|
||||||
|
if [ -z "${LD_PRELOAD+x}" ]; then
|
||||||
|
LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
|
||||||
|
export LD_PRELOAD
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If running the rails server then create or migrate existing database
|
||||||
|
if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
|
||||||
|
./bin/rails db:prepare
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "${@}"
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require_relative "../config/application"
|
||||||
|
require "importmap/commands"
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require_relative "../config/environment"
|
||||||
|
require "solid_queue/cli"
|
||||||
|
|
||||||
|
SolidQueue::Cli.start(ARGV)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user