zenbook migration
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
module test
|
||||
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/go-faker/faker/v4 v4.5.0 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/oapi-codegen/runtime v1.1.1 // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/typesense/typesense-go/v2 v2.0.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-faker/faker/v4 v4.5.0 h1:ARzAY2XoOL9tOUK+KSecUQzyXQsUaZHefjyF8x6YFHc=
|
||||
github.com/go-faker/faker/v4 v4.5.0/go.mod h1:p3oq1GRjG2PZ7yqeFFfQI20Xm61DoBDlCA8RiSyZ48M=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
|
||||
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/typesense/typesense-go/v2 v2.0.0 h1:+MksOnrVioDqsGpz8RXkOUqhVN+yFxZwJlGDQHr/64I=
|
||||
github.com/typesense/typesense-go/v2 v2.0.0/go.mod h1:7V1ZBSfmdciL6yb2bPtWha+W53gV5WZhyOSpVgDJfao=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
@@ -0,0 +1,205 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Typesense Cluster Test</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/typesense@1/dist/typesense.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.column {
|
||||
border: 1px solid #eee;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.record {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
.column-header {
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #eee;
|
||||
}
|
||||
.error-message {
|
||||
color: red;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Typesense Cluster Test Page</h1>
|
||||
<div>
|
||||
<button onclick="addRecord()">Add Random Record</button>
|
||||
<button onclick="loadAllRecords()">Refresh All</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="column">
|
||||
<div class="column-header">
|
||||
<h2>Node 1</h2>
|
||||
<div class="error-message" id="error1"></div>
|
||||
</div>
|
||||
<div id="records1"></div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="column-header">
|
||||
<h2>Node 2</h2>
|
||||
<div class="error-message" id="error2"></div>
|
||||
</div>
|
||||
<div id="records2"></div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="column-header">
|
||||
<h2>Node 3</h2>
|
||||
<div class="error-message" id="error3"></div>
|
||||
</div>
|
||||
<div id="records3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Configuration - Replace these values with your actual Typesense cluster nodes
|
||||
const TYPESENSE_NODES = [
|
||||
'node-stable-1.db.typesenseha.zerops',
|
||||
'node-stable-2.db.typesenseha.zerops',
|
||||
'node-stable-3.db.typesenseha.zerops'
|
||||
];
|
||||
const TYPESENSE_PROTOCOL = 'http';
|
||||
const TYPESENSE_APIKEY = 'I.GQrM0XjqokPFr729Eqo6d8ahvMYp8G';
|
||||
|
||||
// Initialize Typesense clients for each node
|
||||
const clients = TYPESENSE_NODES.map(host => new Typesense.Client({
|
||||
nodes: [{
|
||||
host: host,
|
||||
protocol: TYPESENSE_PROTOCOL,
|
||||
port: 8108,
|
||||
}],
|
||||
apiKey: TYPESENSE_APIKEY,
|
||||
connectionTimeoutSeconds: 3
|
||||
}));
|
||||
|
||||
// Function to add a random record (using the first node)
|
||||
// async function addRecord() {
|
||||
// try {
|
||||
// const record = {
|
||||
// num_employees: Math.floor(Math.random() * 1000) + 1,
|
||||
// created: new Date().toISOString(),
|
||||
// unix: Math.floor(Date.now() / 1000)
|
||||
// };
|
||||
// const response = await clients[0].collections('companies').documents().create(record);
|
||||
// console.log('Record added:', response);
|
||||
// loadAllRecords();
|
||||
// } catch (error) {
|
||||
// console.error('Error adding record:', error);
|
||||
// document.getElementById('error1').textContent = 'Error adding record: ' + error.message;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Utility function for delay
|
||||
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
// Function to add a random record to a random node
|
||||
async function addRecord() {
|
||||
try {
|
||||
// Choose random node
|
||||
const randomNodeIndex = Math.floor(Math.random() * clients.length);
|
||||
const selectedClient = clients[randomNodeIndex];
|
||||
|
||||
const record = {
|
||||
num_employees: Math.floor(Math.random() * 1000) + 1,
|
||||
created: new Date().toLocaleTimeString('en-US', { hour12: false }),
|
||||
unix: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
// Clear previous errors
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
document.getElementById(`error${i + 1}`).textContent = '';
|
||||
}
|
||||
|
||||
// Show which node we're writing to
|
||||
document.getElementById(`error${randomNodeIndex + 1}`).textContent =
|
||||
'✍️ Writing new record to this node...';
|
||||
|
||||
const response = await selectedClient.collections('companies').documents().create(record);
|
||||
console.log(`Record added to node ${randomNodeIndex + 1}:`, response);
|
||||
|
||||
// Wait for 1 second before refreshing
|
||||
await delay(1000);
|
||||
|
||||
// Clear the writing message
|
||||
document.getElementById(`error${randomNodeIndex + 1}`).textContent = '';
|
||||
|
||||
// Refresh all nodes
|
||||
loadAllRecords();
|
||||
} catch (error) {
|
||||
console.error('Error adding record:', error);
|
||||
document.getElementById(`error${randomNodeIndex + 1}`).textContent =
|
||||
'Error adding record: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to load records from a specific node
|
||||
async function loadRecords(clientIndex) {
|
||||
try {
|
||||
document.getElementById(`error${clientIndex + 1}`).textContent = '';
|
||||
const searchParameters = {
|
||||
q: '*',
|
||||
sort_by: 'unix:desc',
|
||||
per_page: 100
|
||||
};
|
||||
const response = await clients[clientIndex].collections('companies').documents().search(searchParameters);
|
||||
|
||||
const recordsDiv = document.getElementById(`records${clientIndex + 1}`);
|
||||
recordsDiv.innerHTML = response.hits.map(hit => `
|
||||
<div class="record">
|
||||
<div>ID: ${hit.document.id}</div>
|
||||
<div>Employees: ${hit.document.num_employees}</div>
|
||||
<div>Created: ${hit.document.created}</div>
|
||||
<div>Unix Timestamp: ${hit.document.unix}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} catch (error) {
|
||||
console.error(`Error loading records from node ${clientIndex + 1}:`, error);
|
||||
document.getElementById(`error${clientIndex + 1}`).textContent =
|
||||
`Error loading records from node ${clientIndex + 1}: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to load records from all nodes
|
||||
function loadAllRecords() {
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
loadRecords(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Load records when the page loads
|
||||
loadAllRecords();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Typesense Test</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/typesense@1/dist/typesense.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
.record {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Typesense Test Page</h1>
|
||||
<button onclick="addRecord()">Add Random Record</button>
|
||||
<h2>Last 100 Records</h2>
|
||||
<div id="records"></div>
|
||||
|
||||
<script>
|
||||
// Configuration - Replace these values with your actual Typesense details
|
||||
const TYPESENSE_HOST = 'typesense-13-8108.app-ranpu.zerops.dev';
|
||||
const TYPESENSE_PROTOCOL = 'https';
|
||||
const TYPESENSE_APIKEY = 'BXCkhnEpwW1Kq0nVcvcWDRtZ75aIXtYy';
|
||||
|
||||
// Initialize Typesense client
|
||||
const client = new Typesense.Client({
|
||||
nodes: [{
|
||||
host: TYPESENSE_HOST,
|
||||
protocol: TYPESENSE_PROTOCOL,
|
||||
}],
|
||||
apiKey: TYPESENSE_APIKEY,
|
||||
connectionTimeoutSeconds: 3
|
||||
});
|
||||
|
||||
// Function to add a random record
|
||||
async function addRecord() {
|
||||
try {
|
||||
const record = {
|
||||
num_employees: Math.floor(Math.random() * 1000) + 1,
|
||||
created: new Date().toISOString(),
|
||||
unix: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
const response = await client.collections('companies').documents().create(record);
|
||||
console.log('Record added:', response);
|
||||
loadRecords(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error('Error adding record:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to load and display records
|
||||
async function loadRecords() {
|
||||
try {
|
||||
const searchParameters = {
|
||||
q: '*',
|
||||
sort_by: 'unix:desc',
|
||||
per_page: 100
|
||||
};
|
||||
|
||||
const response = await client.collections('companies').documents().search(searchParameters);
|
||||
|
||||
const recordsDiv = document.getElementById('records');
|
||||
recordsDiv.innerHTML = response.hits.map(hit => `
|
||||
<div class="record">
|
||||
<div>ID: ${hit.document.id}</div>
|
||||
<div>Employees: ${hit.document.num_employees}</div>
|
||||
<div>Created: ${hit.document.created}</div>
|
||||
<div>Unix Timestamp: ${hit.document.unix}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} catch (error) {
|
||||
console.error('Error loading records:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load records when the page loads
|
||||
loadRecords();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/typesense/typesense-go/v2/typesense"
|
||||
"github.com/typesense/typesense-go/v2/typesense/api"
|
||||
"github.com/typesense/typesense-go/v2/typesense/api/pointer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
var url, apiKey string
|
||||
var list, del bool
|
||||
var delay time.Duration
|
||||
flag.StringVar(&url, "url", "url", "url")
|
||||
flag.StringVar(&apiKey, "api-key", "nope", "api key")
|
||||
flag.BoolVar(&list, "list", false, "whether to list 10 latest documents from collection")
|
||||
flag.BoolVar(&del, "delete", false, "whether delete collection")
|
||||
flag.DurationVar(&delay, "delay", time.Second, "delay between requests")
|
||||
flag.Parse()
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
client := typesense.NewClient(
|
||||
typesense.WithServer(url),
|
||||
typesense.WithAPIKey(apiKey),
|
||||
)
|
||||
|
||||
{
|
||||
ok, err := client.Health(ctx, 5*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("health check failed")
|
||||
}
|
||||
}
|
||||
|
||||
const collectionName = "companies"
|
||||
|
||||
if del {
|
||||
_, err := client.Collection(collectionName).Delete(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("deleted collection: %s\n", collectionName)
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
var exists bool
|
||||
{
|
||||
collections, err := client.Collections().Retrieve(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, collection := range collections {
|
||||
fmt.Printf("listed collection: %s\n", collection.Name)
|
||||
if collection.Name == collectionName {
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
collection, err := client.Collections().Create(ctx, &api.CollectionSchema{
|
||||
Name: "companies",
|
||||
Fields: []api.Field{
|
||||
{
|
||||
Name: "num_employees",
|
||||
Type: "int32",
|
||||
},
|
||||
{
|
||||
Name: "created",
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Name: "unix",
|
||||
Type: "int64",
|
||||
},
|
||||
},
|
||||
DefaultSortingField: pointer.String("unix"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("created collection: %s\n", collection.Name)
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
if list {
|
||||
search, err := client.Collection(collectionName).Documents().Search(ctx, &api.SearchCollectionParams{
|
||||
Q: pointer.String("*"),
|
||||
Limit: pointer.Int(250),
|
||||
SortBy: pointer.String("unix:desc"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range *search.Hits {
|
||||
hit := (*search.Hits)[len(*search.Hits)-1-i]
|
||||
doc := *hit.Document
|
||||
fmt.Printf("LIST: ==> %s (%s)\n", doc["created"], doc["id"])
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
fmt.Println("context canceled")
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
document := map[string]any{
|
||||
"num_employees": rand.Int32N(10000),
|
||||
"created": now.Format(time.TimeOnly),
|
||||
"unix": now.Unix(),
|
||||
}
|
||||
|
||||
var id string
|
||||
documentResponse, err := client.Collection(collectionName).Documents().Create(ctx, document)
|
||||
if err != nil {
|
||||
fmt.Printf("create document failed: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s ==>\n", document["created"])
|
||||
|
||||
id = documentResponse["id"].(string)
|
||||
for {
|
||||
documentResponse, err = client.Collection(collectionName).Document(id).Retrieve(ctx)
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
fmt.Println("context canceled")
|
||||
return nil
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("==> %s (%s)\n", documentResponse["created"], documentResponse["id"])
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
node-stable-1.runtime.typesense.zerops:8107:8108,node-stable-2.runtime.typesense.zerops;:8107:8108,node-stable-3.runtime.typesense.zerops;:8107:8108
|
||||
@@ -0,0 +1,5 @@
|
||||
[server]
|
||||
|
||||
api-key = test
|
||||
data-dir = /var/lib/typesense
|
||||
;nodes = node-list.txt
|
||||
@@ -0,0 +1,9 @@
|
||||
services:
|
||||
- hostname: elasticsearch
|
||||
type: elasticsearch@8.16
|
||||
mode: NON_HA
|
||||
enableSubdomainAccess: true
|
||||
- hostname: typesense
|
||||
type: typesense@27.1
|
||||
mode: NON_HA
|
||||
enableSubdomainAccess: true
|
||||
@@ -0,0 +1,20 @@
|
||||
zerops:
|
||||
- setup: typesense
|
||||
build:
|
||||
buildCommands:
|
||||
- curl -O https://dl.typesense.org/releases/27.1/typesense-server-27.1-linux-amd64.tar.gz
|
||||
- tar -xzf typesense-server-27.1-linux-amd64.tar.gz
|
||||
deployFiles:
|
||||
- typesense-server
|
||||
- typesense-server.ini
|
||||
run:
|
||||
ports:
|
||||
- port: 8107
|
||||
description: "peering"
|
||||
- port: 8108
|
||||
httpSupport: true
|
||||
description: "api"
|
||||
initCommands:
|
||||
- sudo mkdir -p /var/lib/typesense
|
||||
start: zsc noop
|
||||
# start: sudo /var/www/typesense-server --config=/var/www/typesense-server.ini
|
||||
Reference in New Issue
Block a user