resolved conflicts
This commit is contained in:
commit
48351965b7
41 changed files with 920 additions and 257 deletions
|
|
@ -1,18 +1,13 @@
|
|||
from chalicelib.utils import ch_client
|
||||
from .events_pg import *
|
||||
|
||||
|
||||
def __explode_properties(rows):
|
||||
for i in range(len(rows)):
|
||||
rows[i] = {**rows[i], **rows[i]["$properties"]}
|
||||
rows[i].pop("$properties")
|
||||
return rows
|
||||
from chalicelib.utils.exp_ch_helper import explode_dproperties, add_timestamp
|
||||
|
||||
|
||||
def get_customs_by_session_id(session_id, project_id):
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
rows = cur.execute(""" \
|
||||
SELECT `$properties`,
|
||||
properties,
|
||||
created_at,
|
||||
'CUSTOM' AS type
|
||||
FROM product_analytics.events
|
||||
|
|
@ -21,8 +16,10 @@ def get_customs_by_session_id(session_id, project_id):
|
|||
AND `$event_name`!='INCIDENT'
|
||||
ORDER BY created_at;""",
|
||||
{"project_id": project_id, "session_id": session_id})
|
||||
rows = __explode_properties(rows)
|
||||
return helper.list_to_camel_case(rows)
|
||||
rows = helper.list_to_camel_case(rows, ignore_keys=["properties"])
|
||||
rows = explode_dproperties(rows)
|
||||
rows = add_timestamp(rows)
|
||||
return rows
|
||||
|
||||
|
||||
def __merge_cells(rows, start, count, replacement):
|
||||
|
|
@ -69,12 +66,13 @@ def get_by_session_id(session_id, project_id, group_clickrage=False, event_type:
|
|||
parameters={"project_id": project_id, "session_id": session_id,
|
||||
"select_events": select_events})
|
||||
rows = cur.execute(query)
|
||||
rows = __explode_properties(rows)
|
||||
rows = explode_dproperties(rows)
|
||||
if group_clickrage and 'CLICK' in select_events:
|
||||
rows = __get_grouped_clickrage(rows=rows, session_id=session_id, project_id=project_id)
|
||||
|
||||
rows = helper.list_to_camel_case(rows)
|
||||
rows = sorted(rows, key=lambda k: k["createdAt"])
|
||||
rows = add_timestamp(rows)
|
||||
return rows
|
||||
|
||||
|
||||
|
|
@ -91,7 +89,7 @@ def get_incidents_by_session_id(session_id, project_id):
|
|||
ORDER BY created_at;""",
|
||||
parameters={"project_id": project_id, "session_id": session_id})
|
||||
rows = cur.execute(query)
|
||||
rows = __explode_properties(rows)
|
||||
rows = explode_dproperties(rows)
|
||||
rows = helper.list_to_camel_case(rows)
|
||||
rows = sorted(rows, key=lambda k: k["createdAt"])
|
||||
return rows
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from chalicelib.utils import ch_client, helper
|
||||
import datetime
|
||||
from .issues_pg import get_all_types
|
||||
from chalicelib.utils.exp_ch_helper import explode_dproperties, add_timestamp
|
||||
|
||||
|
||||
def get(project_id, issue_id):
|
||||
|
|
@ -21,7 +21,7 @@ def get(project_id, issue_id):
|
|||
def get_by_session_id(session_id, project_id, issue_type=None):
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
query = cur.format(query=f"""\
|
||||
SELECT *
|
||||
SELECT created_at, `$properties`
|
||||
FROM product_analytics.events
|
||||
WHERE session_id = %(session_id)s
|
||||
AND project_id= %(project_id)s
|
||||
|
|
@ -29,8 +29,11 @@ def get_by_session_id(session_id, project_id, issue_type=None):
|
|||
{"AND issue_type = %(type)s" if issue_type is not None else ""}
|
||||
ORDER BY created_at;""",
|
||||
parameters={"session_id": session_id, "project_id": project_id, "type": issue_type})
|
||||
data = cur.execute(query)
|
||||
return helper.list_to_camel_case(data)
|
||||
rows = cur.execute(query)
|
||||
rows = explode_dproperties(rows)
|
||||
rows = helper.list_to_camel_case(rows)
|
||||
rows = add_timestamp(rows)
|
||||
return rows
|
||||
|
||||
|
||||
# To reduce the number of issues in the replay;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import logging
|
||||
import math
|
||||
import re
|
||||
import struct
|
||||
from decimal import Decimal
|
||||
from typing import Union, Any
|
||||
|
||||
import schemas
|
||||
from chalicelib.utils import sql_helper as sh
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from schemas import SearchEventOperator
|
||||
import math
|
||||
import struct
|
||||
from decimal import Decimal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -233,3 +234,16 @@ def best_clickhouse_type(value):
|
|||
return "Float64"
|
||||
|
||||
raise TypeError(f"Unsupported type: {type(value).__name__}")
|
||||
|
||||
|
||||
def explode_dproperties(rows):
|
||||
for i in range(len(rows)):
|
||||
rows[i] = {**rows[i], **rows[i]["$properties"]}
|
||||
rows[i].pop("$properties")
|
||||
return rows
|
||||
|
||||
|
||||
def add_timestamp(rows):
|
||||
for row in rows:
|
||||
row["timestamp"] = TimeUTC.datetime_to_timestamp(row["createdAt"])
|
||||
return rows
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ def random_string(length=36):
|
|||
return "".join(random.choices(string.hexdigits, k=length))
|
||||
|
||||
|
||||
def list_to_camel_case(items: list[dict], flatten: bool = False) -> list[dict]:
|
||||
def list_to_camel_case(items: list[dict], flatten: bool = False, ignore_keys=[]) -> list[dict]:
|
||||
for i in range(len(items)):
|
||||
if flatten:
|
||||
items[i] = flatten_nested_dicts(items[i])
|
||||
items[i] = dict_to_camel_case(items[i])
|
||||
items[i] = dict_to_camel_case(items[i], ignore_keys=[])
|
||||
|
||||
return items
|
||||
|
||||
|
|
|
|||
|
|
@ -733,8 +733,8 @@ func (c *connectorImpl) InsertCustom(session *sessions.Session, msg *messages.Cu
|
|||
if err != nil {
|
||||
return fmt.Errorf("can't marshal custom event: %s", err)
|
||||
}
|
||||
var customPayload interface{}
|
||||
if err := json.Unmarshal([]byte(msg.Payload), customPayload); err != nil {
|
||||
customPayload := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(msg.Payload), &customPayload); err != nil {
|
||||
log.Printf("can't unmarshal custom event payload into object: %s", err)
|
||||
customPayload = map[string]interface{}{
|
||||
"payload": msg.Payload,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function KaiChat() {
|
|||
const chatTitle = kaiStore.chatTitle;
|
||||
const setTitle = kaiStore.setTitle;
|
||||
const userId = userStore.account.id;
|
||||
const userLetter = userStore.account.name[0].toUpperCase();
|
||||
const userName = userStore.account.name;
|
||||
const { activeSiteId } = projectsStore;
|
||||
const [section, setSection] = React.useState<'intro' | 'chat'>('intro');
|
||||
const [threadId, setThreadId] = React.useState<string | null>(null);
|
||||
|
|
@ -44,7 +44,11 @@ function KaiChat() {
|
|||
hideModal();
|
||||
}}
|
||||
/>,
|
||||
{ right: true, width: 300 },
|
||||
{
|
||||
right: true,
|
||||
width: 320,
|
||||
className: 'bg-none flex items-center h-screen',
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -93,44 +97,70 @@ function KaiChat() {
|
|||
const newThread = await kaiService.createKaiChat(activeSiteId);
|
||||
if (newThread) {
|
||||
setThreadId(newThread.toString());
|
||||
kaiStore.setTitle(null);
|
||||
setSection('chat');
|
||||
} else {
|
||||
toast.error("Something wen't wrong. Please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
if (!threadId) return;
|
||||
void kaiStore.cancelGeneration({
|
||||
projectId: activeSiteId,
|
||||
threadId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto" style={{ maxWidth: PANEL_SIZES.maxWidth }}>
|
||||
<div
|
||||
className="w-full mx-auto h-full"
|
||||
style={{ maxWidth: PANEL_SIZES.maxWidth }}
|
||||
>
|
||||
<div
|
||||
className={'w-full rounded-lg overflow-hidden border shadow relative'}
|
||||
className={'w-full rounded-lg overflow-hidden bg-white relative h-full'}
|
||||
>
|
||||
<ChatHeader
|
||||
chatTitle={chatTitle}
|
||||
openChats={openChats}
|
||||
goBack={goBack}
|
||||
onCreate={onCreate}
|
||||
/>
|
||||
<div
|
||||
className={
|
||||
'w-full bg-active-blue flex flex-col items-center justify-center py-4 relative'
|
||||
}
|
||||
style={{
|
||||
height: '70svh',
|
||||
background:
|
||||
'radial-gradient(50% 50% at 50% 50%, var(--color-glassWhite) 0%, var(--color-glassMint) 46%, var(--color-glassLavander) 100%)',
|
||||
}}
|
||||
>
|
||||
{section === 'intro' ? (
|
||||
<IntroSection onAsk={onCreate} />
|
||||
) : (
|
||||
<ChatLog
|
||||
threadId={threadId}
|
||||
projectId={activeSiteId}
|
||||
userLetter={userLetter}
|
||||
chatTitle={chatTitle}
|
||||
initialMsg={initialMsg}
|
||||
setInitialMsg={setInitialMsg}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{section === 'intro' ? (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
'flex flex-col items-center justify-center py-4 relative'
|
||||
}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: 'translateY(-50%)',
|
||||
}}
|
||||
>
|
||||
<IntroSection
|
||||
onCancel={onCancel}
|
||||
onAsk={onCreate}
|
||||
projectId={activeSiteId}
|
||||
userName={userName}
|
||||
/>
|
||||
</div>
|
||||
<div className={'text-disabled-text absolute bottom-4 left-0 right-0 text-center text-sm'}>
|
||||
OpenReplay AI can make mistakes. Verify its outputs.
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<ChatLog
|
||||
threadId={threadId}
|
||||
projectId={activeSiteId}
|
||||
chatTitle={chatTitle}
|
||||
initialMsg={initialMsg}
|
||||
setInitialMsg={setInitialMsg}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export default class KaiService extends AiService {
|
|||
supports_visualization: boolean;
|
||||
chart: string;
|
||||
chart_data: string;
|
||||
sessions?: Record<string, any>[];
|
||||
}[];
|
||||
title: string;
|
||||
}> => {
|
||||
|
|
@ -123,4 +124,19 @@ export default class KaiService extends AiService {
|
|||
const data = await r.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
getPromptSuggestions = async (
|
||||
projectId: string,
|
||||
threadId?: string | null,
|
||||
): Promise<string[]> => {
|
||||
const endpoint = threadId
|
||||
? `/kai/${projectId}/chats/${threadId}/prompt-suggestions`
|
||||
: `/kai/${projectId}/prompt-suggestions`;
|
||||
const r = await this.client.get(endpoint);
|
||||
if (!r.ok) {
|
||||
throw new Error('Failed to fetch prompt suggestions');
|
||||
}
|
||||
const data = await r.json();
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { BotChunk, ChatManager } from './SocketManager';
|
|||
import { kaiService as aiService, kaiService } from 'App/services';
|
||||
import { toast } from 'react-toastify';
|
||||
import Widget from 'App/mstore/types/widget';
|
||||
import Session, { ISession } from '@/types/session/session';
|
||||
|
||||
export interface Message {
|
||||
text: string;
|
||||
|
|
@ -15,6 +16,7 @@ export interface Message {
|
|||
supports_visualization: boolean;
|
||||
feedback: boolean | null;
|
||||
duration: number;
|
||||
sessions?: Session[];
|
||||
}
|
||||
export interface SentMessage
|
||||
extends Omit<
|
||||
|
|
@ -161,6 +163,9 @@ class KaiStore {
|
|||
chart: m.chart,
|
||||
supports_visualization: m.supports_visualization,
|
||||
chart_data: m.chart_data,
|
||||
sessions: m.sessions
|
||||
? m.sessions.map((s) => new Session(s))
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
|
@ -220,6 +225,9 @@ class KaiStore {
|
|||
chart: '',
|
||||
supports_visualization: msg.supports_visualization,
|
||||
chart_data: '',
|
||||
sessions: msg.sessions
|
||||
? msg.sessions.map((s) => new Session(s))
|
||||
: undefined,
|
||||
};
|
||||
this.bumpUsage();
|
||||
this.addMessage(msgObj);
|
||||
|
|
@ -268,7 +276,7 @@ class KaiStore {
|
|||
deleting.push(this.lastKaiMessage.index);
|
||||
}
|
||||
this.deleteAtIndex(deleting);
|
||||
this.setReplacing(false);
|
||||
this.setReplacing(null);
|
||||
}
|
||||
this.addMessage({
|
||||
text: message,
|
||||
|
|
@ -309,7 +317,6 @@ class KaiStore {
|
|||
|
||||
cancelGeneration = async (settings: {
|
||||
projectId: string;
|
||||
userId: string;
|
||||
threadId: string;
|
||||
}) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import io from 'socket.io-client';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ISession } from '@/types/session/session';
|
||||
|
||||
export class ChatManager {
|
||||
socket: ReturnType<typeof io>;
|
||||
|
|
@ -77,9 +78,7 @@ export class ChatManager {
|
|||
msgCallback,
|
||||
titleCallback,
|
||||
}: {
|
||||
msgCallback: (
|
||||
msg: StateEvent | BotChunk,
|
||||
) => void;
|
||||
msgCallback: (msg: StateEvent | BotChunk) => void;
|
||||
titleCallback: (title: string) => void;
|
||||
}) => {
|
||||
this.socket.on('chunk', (msg: BotChunk) => {
|
||||
|
|
@ -111,7 +110,8 @@ export interface BotChunk {
|
|||
messageId: string;
|
||||
duration: number;
|
||||
supports_visualization: boolean;
|
||||
type: 'chunk'
|
||||
sessions?: ISession[];
|
||||
type: 'chunk';
|
||||
}
|
||||
|
||||
interface StateEvent {
|
||||
|
|
|
|||
|
|
@ -1,56 +1,68 @@
|
|||
import React from 'react';
|
||||
import { Icon } from 'UI';
|
||||
import { MessagesSquare, ArrowLeft } from 'lucide-react';
|
||||
import { MessagesSquare, ArrowLeft, SquarePen } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function ChatHeader({
|
||||
openChats = () => {},
|
||||
goBack,
|
||||
chatTitle,
|
||||
onCreate,
|
||||
}: {
|
||||
goBack?: () => void;
|
||||
openChats?: () => void;
|
||||
chatTitle: string | null;
|
||||
onCreate: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
//absolute top-0 left-0 right-0 z-10
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'px-4 py-2 flex items-center bg-white border-b border-b-gray-lighter'
|
||||
}
|
||||
>
|
||||
<div className={'flex-1'}>
|
||||
{goBack ? (
|
||||
<div className="p-4 pb-0 w-full">
|
||||
<div
|
||||
className={'px-4 py-2 flex items-center bg-gray-lightest rounded-lg'}
|
||||
>
|
||||
<div className={'flex-1'}>
|
||||
{goBack ? (
|
||||
<div
|
||||
className={
|
||||
'w-fit flex items-center gap-2 font-semibold cursor-pointer hover:text-main'
|
||||
}
|
||||
onClick={goBack}
|
||||
>
|
||||
<ArrowLeft size={14} />
|
||||
<div>{t('Back')}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon name={'kai-mono'} size={18} />
|
||||
<div className={'font-semibold text-xl'}>Kai</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={'flex items-center gap-2 mx-auto max-w-[80%]'}>
|
||||
{chatTitle ? (
|
||||
<div className="font-semibold text-xl whitespace-nowrap truncate">
|
||||
{chatTitle}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={'flex-1 justify-end flex items-center gap-4'}>
|
||||
{goBack ? (
|
||||
<div
|
||||
onClick={onCreate}
|
||||
className="font-semibold w-fit cursor-pointer hover:text-main flex items-center gap-2"
|
||||
>
|
||||
<SquarePen size={14} />
|
||||
<div>{t('New Chat')}</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={
|
||||
'w-fit flex items-center gap-2 font-semibold cursor-pointer'
|
||||
}
|
||||
onClick={goBack}
|
||||
className="font-semibold w-fit cursor-pointer hover:text-main flex items-center gap-2"
|
||||
onClick={openChats}
|
||||
>
|
||||
<ArrowLeft size={14} />
|
||||
<div>{t('Back')}</div>
|
||||
<MessagesSquare size={14} />
|
||||
<div>{t('Chats')}</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={'flex items-center gap-2 mx-auto max-w-[80%]'}>
|
||||
{chatTitle ? (
|
||||
<div className="font-semibold text-xl whitespace-nowrap truncate">
|
||||
{chatTitle}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Icon name={'kai_colored'} size={18} />
|
||||
<div className={'font-semibold text-xl'}>Kai</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className={'flex-1 justify-end flex items-center gap-2'}>
|
||||
<div
|
||||
className="font-semibold w-fit cursor-pointer flex items-center gap-2"
|
||||
onClick={openChats}
|
||||
>
|
||||
<MessagesSquare size={14} />
|
||||
<div>{t('Chats')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Button, Input, Tooltip } from 'antd';
|
||||
import { SendHorizonal, OctagonX } from 'lucide-react';
|
||||
import { X, ArrowUp } from 'lucide-react';
|
||||
import { kaiStore } from '../KaiStore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import Usage from './Usage';
|
||||
|
|
@ -8,11 +8,13 @@ import Usage from './Usage';
|
|||
function ChatInput({
|
||||
isLoading,
|
||||
onSubmit,
|
||||
threadId,
|
||||
isArea,
|
||||
onCancel,
|
||||
}: {
|
||||
isLoading?: boolean;
|
||||
onSubmit: (str: string) => void;
|
||||
threadId: string;
|
||||
onCancel: () => void;
|
||||
isArea?: boolean;
|
||||
}) {
|
||||
const inputRef = React.useRef<typeof Input>(null);
|
||||
const usage = kaiStore.usage;
|
||||
|
|
@ -28,8 +30,7 @@ function ChatInput({
|
|||
return;
|
||||
}
|
||||
if (isProcessing) {
|
||||
const settings = { projectId: '2325', userId: '0', threadId };
|
||||
void kaiStore.cancelGeneration(settings);
|
||||
onCancel();
|
||||
} else {
|
||||
if (inputValue.length > 0) {
|
||||
onSubmit(inputValue);
|
||||
|
|
@ -50,7 +51,34 @@ function ChatInput({
|
|||
}, [inputValue]);
|
||||
|
||||
const isReplacing = kaiStore.replacing !== null;
|
||||
|
||||
const placeholder = limited
|
||||
? `You've reached the daily limit for queries, come again tomorrow!`
|
||||
: 'Ask anything about your product and users...';
|
||||
if (isArea) {
|
||||
return (
|
||||
<div className="relative">
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
className="!resize-none rounded-lg shadow"
|
||||
onPressEnter={submit}
|
||||
ref={inputRef}
|
||||
placeholder={placeholder}
|
||||
size={'large'}
|
||||
disabled={limited}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
/>
|
||||
<div className="absolute bottom-2 right-2">
|
||||
<SendButton
|
||||
isLoading={isLoading}
|
||||
submit={submit}
|
||||
limited={limited}
|
||||
isProcessing={isProcessing}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="relative">
|
||||
<Input
|
||||
|
|
@ -61,12 +89,9 @@ function ChatInput({
|
|||
}
|
||||
}}
|
||||
ref={inputRef}
|
||||
placeholder={
|
||||
limited
|
||||
? `You've reached the daily limit for queries, come again tomorrow!`
|
||||
: 'Ask anything about your product and users...'
|
||||
}
|
||||
placeholder={placeholder}
|
||||
size={'large'}
|
||||
className="rounded-lg shadow"
|
||||
disabled={limited}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
|
|
@ -76,31 +101,19 @@ function ChatInput({
|
|||
<Tooltip title={'Cancel Editing'}>
|
||||
<Button
|
||||
onClick={cancelReplace}
|
||||
icon={<OctagonX size={16} />}
|
||||
type={'text'}
|
||||
icon={<X size={16} />}
|
||||
size={'small'}
|
||||
shape={'circle'}
|
||||
disabled={limited}
|
||||
/>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
<Tooltip title={'Send message'}>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
onClick={submit}
|
||||
disabled={limited}
|
||||
icon={
|
||||
isProcessing ? (
|
||||
<OctagonX size={16} />
|
||||
) : (
|
||||
<SendHorizonal size={16} />
|
||||
)
|
||||
}
|
||||
type={'text'}
|
||||
size={'small'}
|
||||
shape={'circle'}
|
||||
/>
|
||||
</Tooltip>
|
||||
<SendButton
|
||||
isLoading={isLoading}
|
||||
submit={submit}
|
||||
limited={limited}
|
||||
isProcessing={isProcessing}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
@ -111,4 +124,42 @@ function ChatInput({
|
|||
);
|
||||
}
|
||||
|
||||
function SendButton({
|
||||
isLoading,
|
||||
submit,
|
||||
limited,
|
||||
isProcessing,
|
||||
}: {
|
||||
isLoading?: boolean;
|
||||
submit: () => void;
|
||||
limited: boolean;
|
||||
isProcessing?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Tooltip title={isProcessing ? 'Cancel processing' : 'Send message'}>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
onClick={submit}
|
||||
disabled={limited}
|
||||
icon={
|
||||
isProcessing ? (
|
||||
<X size={16} strokeWidth={2} />
|
||||
) : (
|
||||
<div className="bg-[#fff] text-main rounded-full">
|
||||
<ArrowUp size={14} strokeWidth={2} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
type={'primary'}
|
||||
size={'small'}
|
||||
shape={isProcessing ? 'circle' : 'round'}
|
||||
iconPosition={'end'}
|
||||
className="font-semibold text-[#fff]"
|
||||
>
|
||||
{isProcessing ? null : 'Ask'}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(ChatInput);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import ChatInput from './ChatInput';
|
||||
import ChatMsg, { ChatNotice } from './ChatMsg';
|
||||
import Ideas from './Ideas';
|
||||
import { Loader } from 'UI';
|
||||
import { kaiStore } from '../KaiStore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
|
@ -8,17 +9,17 @@ import { observer } from 'mobx-react-lite';
|
|||
function ChatLog({
|
||||
projectId,
|
||||
threadId,
|
||||
userLetter,
|
||||
initialMsg,
|
||||
chatTitle,
|
||||
setInitialMsg,
|
||||
onCancel,
|
||||
}: {
|
||||
projectId: string;
|
||||
threadId: any;
|
||||
userLetter: string;
|
||||
initialMsg: string | null;
|
||||
setInitialMsg: (msg: string | null) => void;
|
||||
chatTitle: string | null;
|
||||
onCancel: () => void;
|
||||
}) {
|
||||
const messages = kaiStore.messages;
|
||||
const loading = kaiStore.loadingChat;
|
||||
|
|
@ -50,11 +51,15 @@ function ChatLog({
|
|||
});
|
||||
}, [messages.length, processingStage]);
|
||||
|
||||
const lastHumanMsgInd: null | number = kaiStore.lastHumanMessage.index;
|
||||
const lastKaiMessageInd: null | number = kaiStore.lastKaiMessage.index;
|
||||
const lastHumanMsgInd: number | null = kaiStore.lastHumanMessage.index;
|
||||
const showIdeas =
|
||||
!processingStage && lastKaiMessageInd === messages.length - 1;
|
||||
return (
|
||||
<Loader loading={loading} className={'w-full h-full'}>
|
||||
<div
|
||||
ref={chatRef}
|
||||
style={{ maxHeight: 'calc(100svh - 165px)' }}
|
||||
className={
|
||||
'overflow-y-auto relative flex flex-col items-center justify-between w-full h-full'
|
||||
}
|
||||
|
|
@ -63,7 +68,6 @@ function ChatLog({
|
|||
{messages.map((msg, index) => (
|
||||
<React.Fragment key={msg.messageId ?? index}>
|
||||
<ChatMsg
|
||||
userName={userLetter}
|
||||
siteId={projectId}
|
||||
message={msg}
|
||||
chatTitle={chatTitle}
|
||||
|
|
@ -81,9 +85,16 @@ function ChatLog({
|
|||
duration={processingStage.duration}
|
||||
/>
|
||||
) : null}
|
||||
{showIdeas ? (
|
||||
<Ideas
|
||||
onClick={(query) => onSubmit(query)}
|
||||
projectId={projectId}
|
||||
threadId={threadId}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={'sticky bottom-0 pt-6 w-2/3'}>
|
||||
<ChatInput onSubmit={onSubmit} threadId={threadId} />
|
||||
<div className={'sticky bottom-0 pt-6 w-2/3 z-50'}>
|
||||
<ChatInput onCancel={onCancel} onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Icon, CopyButton } from 'UI';
|
||||
import { CopyButton } from 'UI';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import cn from 'classnames';
|
||||
import Markdown from 'react-markdown';
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
Loader,
|
||||
ThumbsUp,
|
||||
ThumbsDown,
|
||||
ListRestart,
|
||||
SquarePen,
|
||||
FileDown,
|
||||
Clock,
|
||||
ChartLine,
|
||||
|
|
@ -20,6 +20,7 @@ import { durationFormatted } from 'App/date';
|
|||
import WidgetChart from '@/components/Dashboard/components/WidgetChart';
|
||||
import Widget from 'App/mstore/types/widget';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SessionItem from 'Shared/SessionItem';
|
||||
|
||||
function ChatMsg({
|
||||
userName,
|
||||
|
|
@ -168,28 +169,12 @@ function ChatMsg({
|
|||
}, [metricData, chart_data]);
|
||||
return (
|
||||
<div className={cn('flex gap-2', isUser ? 'flex-row-reverse' : 'flex-row')}>
|
||||
{isUser ? (
|
||||
<div
|
||||
className={
|
||||
'rounded-full bg-main text-white min-w-8 min-h-8 max-h-8 max-w-8 flex items-center justify-center sticky top-0 mt-2 shadow'
|
||||
}
|
||||
>
|
||||
<span className={'font-semibold'}>{userName}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={
|
||||
'rounded-full bg-gray-lightest shadow min-w-8 min-h-8 max-h-8 max-w-8 flex items-center justify-center sticky top-0 mt-2'
|
||||
}
|
||||
>
|
||||
<Icon name={'kai_colored'} size={18} />
|
||||
</div>
|
||||
)}
|
||||
<div className={'mt-1 flex flex-col'}>
|
||||
<div className={'mt-1 flex flex-col group/actions'}>
|
||||
<div
|
||||
className={cn(
|
||||
'markdown-body',
|
||||
isEditing ? 'border-l border-l-main pl-2' : '',
|
||||
isUser ? 'bg-gray-lighter px-4 rounded-full' : '',
|
||||
isEditing ? '!bg-active-blue' : '',
|
||||
)}
|
||||
data-openreplay-obscured
|
||||
ref={bodyRef}
|
||||
|
|
@ -204,32 +189,42 @@ function ChatMsg({
|
|||
<WidgetChart metric={metric} isPreview height={360} />
|
||||
</div>
|
||||
) : null}
|
||||
{message.sessions ? (
|
||||
<div className="flex flex-col">
|
||||
{message.sessions.map((session) => (
|
||||
<div className="shadow border rounded-xl overflow-hidden mb-2">
|
||||
<SessionItem key={session.sessionId} session={session} slim />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{isUser ? (
|
||||
<>
|
||||
<div
|
||||
onClick={onEdit}
|
||||
className={cn(
|
||||
'ml-auto flex items-center gap-2 px-2',
|
||||
'rounded-lg border border-gray-medium text-sm cursor-pointer',
|
||||
'hover:border-main hover:text-main w-fit',
|
||||
canEdit && !isEditing ? '' : 'hidden',
|
||||
)}
|
||||
>
|
||||
<ListRestart size={16} />
|
||||
<div>{t('Edit')}</div>
|
||||
</div>
|
||||
<div className="invisible group-hover/actions:visible mt-2">
|
||||
<Tooltip title={t('Edit')}>
|
||||
<div
|
||||
onClick={onEdit}
|
||||
className={cn(
|
||||
'ml-auto flex items-center gap-2 px-2',
|
||||
'rounded-lg cursor-pointer',
|
||||
'hover:text-main w-fit',
|
||||
canEdit && !isEditing ? '' : 'hidden',
|
||||
)}
|
||||
>
|
||||
<SquarePen size={16} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div
|
||||
onClick={onCancelEdit}
|
||||
className={cn(
|
||||
'ml-auto flex items-center gap-2 px-2',
|
||||
'rounded-lg border border-gray-medium text-sm cursor-pointer',
|
||||
'rounded-lg border border-gray-medium text-xs cursor-pointer',
|
||||
'hover:border-main hover:text-main w-fit',
|
||||
isEditing ? '' : 'hidden',
|
||||
)}
|
||||
>
|
||||
<div>{t('Cancel')}</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{duration ? <MsgDuration duration={duration} /> : null}
|
||||
|
|
|
|||
|
|
@ -44,10 +44,13 @@ function ChatsModal({
|
|||
refetch();
|
||||
};
|
||||
return (
|
||||
<div className={'h-screen w-full flex flex-col gap-2 p-4'}>
|
||||
<div
|
||||
className={'flex flex-col gap-2 p-4 mr-1 rounded-lg bg-white my-auto'}
|
||||
style={{ height: '95svh', width: 310 }}
|
||||
>
|
||||
<div className={'flex items-center font-semibold text-lg gap-2'}>
|
||||
<MessagesSquare size={16} />
|
||||
<span>{t('Chats')}</span>
|
||||
<span>{t('Previous Chats')}</span>
|
||||
</div>
|
||||
{usage.percent > 80 ? (
|
||||
<div className="text-red text-sm">
|
||||
|
|
|
|||
|
|
@ -1,30 +1,72 @@
|
|||
import React from 'react';
|
||||
import { Lightbulb, MoveRight } from 'lucide-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { kaiService } from 'App/services';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function Ideas({ onClick }: { onClick: (query: string) => void }) {
|
||||
function Ideas({
|
||||
onClick,
|
||||
projectId,
|
||||
threadId = null,
|
||||
}: {
|
||||
onClick: (query: string) => void;
|
||||
projectId: string;
|
||||
threadId?: string | null;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { data: suggestedPromptIdeas = [], isPending } = useQuery({
|
||||
queryKey: ['kai', projectId, 'chats', threadId, 'prompt-suggestions'],
|
||||
queryFn: () => kaiService.getPromptSuggestions(projectId, threadId),
|
||||
staleTime: 1000 * 60,
|
||||
});
|
||||
const ideas = React.useMemo(() => {
|
||||
const defaultPromptIdeas = [
|
||||
'Top user journeys',
|
||||
'Where do users drop off',
|
||||
'Failed network requests today',
|
||||
];
|
||||
const result = suggestedPromptIdeas;
|
||||
const targetSize = 3;
|
||||
while (result.length < targetSize && defaultPromptIdeas.length) {
|
||||
result.push(defaultPromptIdeas.pop());
|
||||
}
|
||||
return result;
|
||||
}, [suggestedPromptIdeas.length]);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className={'flex items-center gap-2 mb-1 text-gray-dark'}>
|
||||
<Lightbulb size={16} />
|
||||
<b>Ideas:</b>
|
||||
<b>Suggested Ideas:</b>
|
||||
</div>
|
||||
<IdeaItem onClick={onClick} title={'Top user journeys'} />
|
||||
<IdeaItem onClick={onClick} title={'Where do users drop off'} />
|
||||
<IdeaItem onClick={onClick} title={'Failed network requests today'} />
|
||||
</>
|
||||
{isPending ? (
|
||||
<div className="animate-pulse text-disabled-text">
|
||||
{t('Generating ideas')}...
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{ideas.map((title) => (
|
||||
<IdeaItem key={title} onClick={onClick} title={title} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IdeaItem({ title, onClick }: { title: string, onClick: (query: string) => void }) {
|
||||
function IdeaItem({
|
||||
title,
|
||||
onClick,
|
||||
}: {
|
||||
title: string;
|
||||
onClick: (query: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
onClick={() => onClick(title)}
|
||||
className={
|
||||
'flex items-center gap-2 cursor-pointer text-gray-dark hover:text-black'
|
||||
'cursor-pointer text-gray-dark hover:text-black rounded-full px-4 py-2 shadow border'
|
||||
}
|
||||
>
|
||||
<MoveRight size={16} />
|
||||
<span>{title}</span>
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,23 +2,33 @@ import React from 'react';
|
|||
import ChatInput from './ChatInput';
|
||||
import Ideas from './Ideas';
|
||||
|
||||
function IntroSection({ onAsk }: { onAsk: (query: string) => void }) {
|
||||
function IntroSection({
|
||||
onAsk,
|
||||
onCancel,
|
||||
userName,
|
||||
projectId,
|
||||
}: {
|
||||
onAsk: (query: string) => void;
|
||||
projectId: string;
|
||||
onCancel: () => void;
|
||||
userName: string;
|
||||
}) {
|
||||
const isLoading = false;
|
||||
return (
|
||||
<>
|
||||
<div className={'text-disabled-text text-xl absolute top-4'}>
|
||||
Kai is your AI assistant, delivering smart insights in response to your
|
||||
queries.
|
||||
</div>
|
||||
<div className={'relative w-2/3'} style={{ height: 44 }}>
|
||||
{/*<GradientBorderInput placeholder={'Ask anything about your product and users...'} onButtonClick={() => null} />*/}
|
||||
<ChatInput isLoading={isLoading} onSubmit={onAsk} />
|
||||
<div className={'absolute top-full flex flex-col gap-2 mt-4'}>
|
||||
<Ideas onClick={(query) => onAsk(query)} />
|
||||
<div className={'relative w-2/3 flex flex-col gap-4'}>
|
||||
<div className="font-semibold text-lg">
|
||||
Hey {userName}, how can I help you?
|
||||
</div>
|
||||
<ChatInput
|
||||
onCancel={onCancel}
|
||||
isLoading={isLoading}
|
||||
onSubmit={onAsk}
|
||||
isArea
|
||||
/>
|
||||
<div className={'absolute top-full flex flex-col gap-2 mt-4'}>
|
||||
<Ideas onClick={(query) => onAsk(query)} projectId={projectId} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={'text-disabled-text absolute bottom-4'}>
|
||||
OpenReplay AI can make mistakes. Verify its outputs.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// @ts-nocheck
|
||||
import React, { Component, createContext } from 'react';
|
||||
import Modal from './Modal';
|
||||
import { className } from '@medv/finder';
|
||||
|
||||
const ModalContext = createContext({
|
||||
component: null,
|
||||
|
|
@ -29,6 +30,7 @@ export class ModalProvider extends Component {
|
|||
this.setState({
|
||||
component,
|
||||
props,
|
||||
className: props.className || undefined,
|
||||
});
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
document.querySelector('body').style.overflow = 'hidden';
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ interface Props {
|
|||
bookmarked?: boolean;
|
||||
toggleFavorite?: (sessionId: string) => void;
|
||||
query?: string;
|
||||
slim?: boolean;
|
||||
}
|
||||
|
||||
const PREFETCH_STATE = {
|
||||
|
|
@ -99,6 +100,7 @@ function SessionItem(props: RouteComponentProps & Props) {
|
|||
isDisabled,
|
||||
live: propsLive,
|
||||
isAdd,
|
||||
slim,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
|
|
@ -261,7 +263,7 @@ function SessionItem(props: RouteComponentProps & Props) {
|
|||
}
|
||||
>
|
||||
<div
|
||||
className={cn(stl.sessionItem, 'flex flex-col p-4')}
|
||||
className={cn(stl.sessionItem, 'flex flex-col', slim ? 'px-4 py-2' : 'p-4')}
|
||||
id="session-item"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseEnter={handleHover}
|
||||
|
|
@ -343,7 +345,7 @@ function SessionItem(props: RouteComponentProps & Props) {
|
|||
: 'Event'}
|
||||
</span>
|
||||
</div>
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<Icon name="circle-fill" size={3} className="mx-2" />
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
|
|
@ -373,7 +375,7 @@ function SessionItem(props: RouteComponentProps & Props) {
|
|||
</span>
|
||||
)}
|
||||
{userOs && userBrowser && (
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<Icon name="circle-fill" size={3} className="mx-2" />
|
||||
)}
|
||||
{userOs && (
|
||||
<span
|
||||
|
|
@ -387,7 +389,7 @@ function SessionItem(props: RouteComponentProps & Props) {
|
|||
</span>
|
||||
)}
|
||||
{userOs && (
|
||||
<Icon name="circle-fill" size={3} className="mx-4" />
|
||||
<Icon name="circle-fill" size={3} className="mx-2" />
|
||||
)}
|
||||
<span className="capitalize" style={{ maxWidth: '70px' }}>
|
||||
<TextEllipsis
|
||||
|
|
|
|||
|
|
@ -354,6 +354,7 @@ export { default as Integrations_teams } from './integrations_teams';
|
|||
export { default as Integrations_vuejs } from './integrations_vuejs';
|
||||
export { default as Integrations_zustand } from './integrations_zustand';
|
||||
export { default as Journal_code } from './journal_code';
|
||||
export { default as Kai_mono } from './kai_mono';
|
||||
export { default as Kai } from './kai';
|
||||
export { default as Kai_colored } from './kai_colored';
|
||||
export { default as Key } from './key';
|
||||
|
|
|
|||
18
frontend/app/components/ui/Icons/kai_mono.tsx
Normal file
18
frontend/app/components/ui/Icons/kai_mono.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* Auto-generated, do not edit */
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
size?: number | string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
fill?: string;
|
||||
}
|
||||
|
||||
function Kai_mono(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 20 20" fill="none" width={ `${ width }px` } height={ `${ height }px` } ><g fill="#394DFE"><path d="M13.125 6.458a.625.625 0 1 0-1.25 0c0 2.022-.447 3.335-1.264 4.153-.818.817-2.131 1.264-4.153 1.264a.625.625 0 1 0 0 1.25c2.022 0 3.335.447 4.152 1.264.818.818 1.265 2.131 1.265 4.153a.625.625 0 1 0 1.25 0c0-2.022.447-3.335 1.264-4.153.818-.817 2.131-1.264 4.153-1.264a.625.625 0 1 0 0-1.25c-2.022 0-3.335-.447-4.153-1.264-.817-.818-1.264-2.131-1.264-4.153ZM6.042 1.458a.625.625 0 1 0-1.25 0c0 1.298-.288 2.09-.766 2.568-.478.478-1.27.766-2.568.766a.625.625 0 1 0 0 1.25c1.298 0 2.09.287 2.568.765.478.478.766 1.27.766 2.568a.625.625 0 0 0 1.25 0c0-1.298.287-2.09.765-2.568.478-.478 1.27-.765 2.568-.765a.625.625 0 0 0 0-1.25c-1.298 0-2.09-.288-2.568-.766-.478-.478-.765-1.27-.765-2.568Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h20v20H0z"/></clipPath></defs></svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default Kai_mono;
|
||||
|
|
@ -356,6 +356,7 @@ import {
|
|||
Integrations_vuejs,
|
||||
Integrations_zustand,
|
||||
Journal_code,
|
||||
Kai_mono,
|
||||
Kai,
|
||||
Kai_colored,
|
||||
Key,
|
||||
|
|
@ -1565,6 +1566,9 @@ const SVG = (props: Props) => {
|
|||
// case 'journal-code':
|
||||
case 'journal-code': return <Journal_code width={ width } height={ height } fill={ fill } />;
|
||||
|
||||
// case 'kai-mono':
|
||||
case 'kai-mono': return <Kai_mono width={ width } height={ height } fill={ fill } />;
|
||||
|
||||
|
||||
case 'kai': return <Kai width={ width } height={ height } fill={ fill } />;
|
||||
|
||||
|
|
|
|||
11
frontend/app/svg/icons/kai-mono.svg
Normal file
11
frontend/app/svg/icons/kai-mono.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8_72)">
|
||||
<path d="M13.1249 6.45831C13.1249 6.11313 12.8451 5.83331 12.4999 5.83331C12.1547 5.83331 11.8749 6.11313 11.8749 6.45831C11.8749 8.47982 11.4283 9.7927 10.6105 10.6106C9.79264 11.4284 8.47976 11.875 6.45825 11.875C6.11307 11.875 5.83325 12.1548 5.83325 12.5C5.83325 12.8452 6.11307 13.125 6.45825 13.125C8.47976 13.125 9.79264 13.5716 10.6105 14.3894C11.4283 15.2073 11.8749 16.5201 11.8749 18.5416C11.8749 18.8868 12.1547 19.1666 12.4999 19.1666C12.8451 19.1666 13.1249 18.8868 13.1249 18.5416C13.1249 16.5201 13.5715 15.2073 14.3893 14.3894C15.2072 13.5716 16.5201 13.125 18.5416 13.125C18.8868 13.125 19.1666 12.8452 19.1666 12.5C19.1666 12.1548 18.8868 11.875 18.5416 11.875C16.5201 11.875 15.2072 11.4284 14.3893 10.6106C13.5715 9.7927 13.1249 8.47982 13.1249 6.45831Z" fill="#394DFE"/>
|
||||
<path d="M6.04158 1.45831C6.04158 1.11313 5.76176 0.833313 5.41658 0.833313C5.07141 0.833313 4.79158 1.11313 4.79158 1.45831C4.79158 2.75644 4.50416 3.54849 4.02629 4.02635C3.54843 4.50422 2.75638 4.79165 1.45825 4.79165C1.11307 4.79165 0.833252 5.07147 0.833252 5.41665C0.833252 5.76182 1.11307 6.04165 1.45825 6.04165C2.75638 6.04165 3.54843 6.32907 4.02629 6.80694C4.50416 7.2848 4.79158 8.07685 4.79158 9.37498C4.79158 9.72016 5.07141 9.99998 5.41658 9.99998C5.76176 9.99998 6.04158 9.72016 6.04158 9.37498C6.04158 8.07685 6.32901 7.2848 6.80688 6.80694C7.28474 6.32907 8.07679 6.04165 9.37492 6.04165C9.7201 6.04165 9.99992 5.76182 9.99992 5.41665C9.99992 5.07147 9.7201 4.79165 9.37492 4.79165C8.07679 4.79165 7.28474 4.50422 6.80688 4.02635C6.32901 3.54849 6.04158 2.75644 6.04158 1.45831Z" fill="#394DFE"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_8_72">
|
||||
<rect width="20" height="20" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -1,3 +1,4 @@
|
|||
{{- if .Values.configOverride.serverConfig }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
|
|
@ -8,6 +9,7 @@ data:
|
|||
{{ $filename }}: |-
|
||||
{{ $content | indent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
|
|
|
|||
|
|
@ -87,27 +87,26 @@ storageSize: 100Gi
|
|||
configOverride:
|
||||
serverConfig:
|
||||
zz-server-override.xml: |-
|
||||
# <clickhouse>
|
||||
# <logger>
|
||||
# <level>information</level>
|
||||
# <console>true</console>
|
||||
# <log remove="remove"></log>
|
||||
# <errorlog remove="remove"></errorlog>
|
||||
# </logger>
|
||||
# <listen_host>0.0.0.0</listen_host>
|
||||
# <keep_alive_timeout>100</keep_alive_timeout>
|
||||
# <concurrent_threads_soft_limit_num>64</concurrent_threads_soft_limit_num>
|
||||
# <concurrent_threads_soft_limit_ratio_to_cores>2</concurrent_threads_soft_limit_ratio_to_cores>
|
||||
# <concurrent_threads_scheduler>fair_round_robin</concurrent_threads_scheduler>
|
||||
# <max_server_memory_usage>102400000000</max_server_memory_usage>
|
||||
# <max_thread_pool_size>10000</max_thread_pool_size>
|
||||
# <max_server_memory_usage_to_ram_ratio>0.8</max_server_memory_usage_to_ram_ratio>
|
||||
# <uncompressed_cache_size remove="remove"></uncompressed_cache_size>
|
||||
# <mmap_cache_size>26214</mmap_cache_size>
|
||||
# </clickhouse>
|
||||
<clickhouse>
|
||||
<logger>
|
||||
<level>information</level>
|
||||
<console>true</console>
|
||||
<log remove="remove"></log>
|
||||
<errorlog remove="remove"></errorlog>
|
||||
</logger>
|
||||
<listen_host>0.0.0.0</listen_host>
|
||||
<keep_alive_timeout>100</keep_alive_timeout>
|
||||
</clickhouse>
|
||||
# another-config.xml: |-
|
||||
# <clickhouse>
|
||||
# <another_setting>value</another_setting>
|
||||
# <concurrent_threads_soft_limit_num>64</concurrent_threads_soft_limit_num>
|
||||
# <concurrent_threads_soft_limit_ratio_to_cores>2</concurrent_threads_soft_limit_ratio_to_cores>
|
||||
# <concurrent_threads_scheduler>fair_round_robin</concurrent_threads_scheduler>
|
||||
# <max_server_memory_usage>102400000000</max_server_memory_usage>
|
||||
# <max_thread_pool_size>10000</max_thread_pool_size>
|
||||
# <max_server_memory_usage_to_ram_ratio>0.8</max_server_memory_usage_to_ram_ratio>
|
||||
# <uncompressed_cache_size remove="remove"></uncompressed_cache_size>
|
||||
# <mmap_cache_size>26214</mmap_cache_size>
|
||||
# </clickhouse>
|
||||
userConfig:
|
||||
zz-user-override.xml: |-
|
||||
|
|
|
|||
|
|
@ -37,3 +37,7 @@ dependencies:
|
|||
repository: file://charts/connector
|
||||
version: 0.1.1
|
||||
condition: connector.enabled
|
||||
- name: assist-api
|
||||
repository: file://charts/assist-api
|
||||
version: 0.1.1
|
||||
condition: assist-api.enabled
|
||||
|
|
|
|||
23
scripts/helmcharts/openreplay/charts/assist-api/.helmignore
Normal file
23
scripts/helmcharts/openreplay/charts/assist-api/.helmignore
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
24
scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml
Normal file
24
scripts/helmcharts/openreplay/charts/assist-api/Chart.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: v2
|
||||
name: assist-api
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rassist-apiing
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.1
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
AppVersion: "v1.22.0"
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "assist-api.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "assist-api.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "assist-api.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "assist-api.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "assist-api.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "assist-api.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "assist-api.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "assist-api.labels" -}}
|
||||
helm.sh/chart: {{ include "assist-api.chart" . }}
|
||||
{{ include "assist-api.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- if .Values.global.appLabels }}
|
||||
{{- .Values.global.appLabels | toYaml | nindent 0}}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "assist-api.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "assist-api.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "assist-api.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "assist-api.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "assist-api.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "assist-api.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "assist-api.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "assist-api.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
shareProcessNamespace: true
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
{{- if .Values.global.enterpriseEditionLicense }}
|
||||
image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}-ee"
|
||||
{{- else }}
|
||||
image: "{{ tpl .Values.image.repository . }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
{{- end }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
{{- if .Values.healthCheck}}
|
||||
{{- .Values.healthCheck | toYaml | nindent 10}}
|
||||
{{- end}}
|
||||
env:
|
||||
- name: LICENSE_KEY
|
||||
value: '{{ .Values.global.enterpriseEditionLicense }}'
|
||||
- name: ASSIST_KEY
|
||||
value: {{ .Values.global.assistKey }}
|
||||
- name: pg_password
|
||||
{{- if .Values.global.postgresql.existingSecret }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.global.postgresql.existingSecret }}
|
||||
key: postgresql-postgres-password
|
||||
{{- else }}
|
||||
value: '{{ .Values.global.postgresql.postgresqlPassword }}'
|
||||
{{- end}}
|
||||
- name: POSTGRES_STRING
|
||||
value: 'postgres://{{ .Values.global.postgresql.postgresqlUser }}:$(pg_password)@{{ .Values.global.postgresql.postgresqlHost }}:{{ .Values.global.postgresql.postgresqlPort }}/{{ .Values.global.postgresql.postgresqlDatabase }}'
|
||||
- name: REDIS_CACHE_ENABLED
|
||||
value: {{ if .Values.global.enterpriseEditionLicense }}"true"{{ else }}"false"{{ end }}
|
||||
{{- include "openreplay.env.redis_string" .Values.global.redis | nindent 12 }}
|
||||
{{- range $key, $val := .Values.global.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
{{- end }}
|
||||
{{- range $key, $val := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
{{- end}}
|
||||
ports:
|
||||
{{- range $key, $val := .Values.service.ports }}
|
||||
- name: {{ $key }}
|
||||
containerPort: {{ $val }}
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
{{- include "openreplay.volume.redis_ca_certificate.mount" .Values.global.redis | nindent 12 }}
|
||||
{{- with .Values.persistence.mounts }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumes:
|
||||
{{- with .Values.persistence.volumes }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- include "openreplay.volume.redis_ca_certificate" .Values.global.redis | nindent 8 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "assist-api.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "assist-api.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "assist-api.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
{{- range $key, $val := .Values.service.ports }}
|
||||
- port: {{ $val }}
|
||||
targetPort: {{ $key }}
|
||||
protocol: TCP
|
||||
name: {{ $key }}
|
||||
{{- end}}
|
||||
selector:
|
||||
{{- include "assist-api.selectorLabels" . | nindent 4 }}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{{- if and ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ( .Values.serviceMonitor.enabled ) }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ include "assist-api.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
{{- if .Values.serviceMonitor.additionalLabels }}
|
||||
{{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
endpoints:
|
||||
{{- .Values.serviceMonitor.scrapeConfigs | toYaml | nindent 4 }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "assist-api.selectorLabels" . | nindent 6 }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "assist-api.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "assist-api.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "assist-api.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "assist-api.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
||||
119
scripts/helmcharts/openreplay/charts/assist-api/values.yaml
Normal file
119
scripts/helmcharts/openreplay/charts/assist-api/values.yaml
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Default values for openreplay.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: "{{ .Values.global.openReplayContainerRegistry }}/assist-api"
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: "assist-api"
|
||||
fullnameOverride: "assist-api-openreplay"
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
securityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
podSecurityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
# podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
# securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
#service:
|
||||
# type: ClusterIP
|
||||
# port: 9000
|
||||
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
additionalLabels:
|
||||
release: observability
|
||||
scrapeConfigs:
|
||||
- port: metrics
|
||||
honorLabels: true
|
||||
interval: 15s
|
||||
path: /metrics
|
||||
scheme: http
|
||||
scrapeTimeout: 10s
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
http: 8080
|
||||
metrics: 8888
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}"
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
tls:
|
||||
secretName: openreplay-ssl
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
env:
|
||||
CLEAR_SOCKET_TIME: 720
|
||||
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
|
||||
persistence: {}
|
||||
# # Spec of spec.template.spec.containers[*].volumeMounts
|
||||
# mounts:
|
||||
# - name: kafka-ssl
|
||||
# mountPath: /opt/kafka/ssl
|
||||
# # Spec of spec.template.spec.volumes
|
||||
# volumes:
|
||||
# - name: kafka-ssl
|
||||
# secret:
|
||||
# secretName: kafka-ssl
|
||||
|
|
@ -49,32 +49,18 @@ spec:
|
|||
value: {{ .Values.global.assistKey }}
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: "{{ .Values.global.s3.region }}"
|
||||
- name: S3_HOST
|
||||
{{- if contains "minio" .Values.global.s3.endpoint }}
|
||||
value: '{{ ternary "https" "http" .Values.global.ORSecureAccess}}://{{ .Values.global.domainName }}:{{ ternary .Values.global.ingress.controller.service.ports.https .Values.global.ingress.controller.service.ports.http .Values.global.ORSecureAccess }}'
|
||||
{{- else}}
|
||||
value: '{{ .Values.global.s3.endpoint }}'
|
||||
{{- end}}
|
||||
- name: S3_KEY
|
||||
{{- if .Values.global.s3.existingSecret }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.global.s3.existingSecret }}
|
||||
key: access-key
|
||||
{{- else }}
|
||||
value: {{ .Values.global.s3.accessKey }}
|
||||
{{- end }}
|
||||
- name: S3_SECRET
|
||||
{{- if .Values.global.s3.existingSecret }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.global.s3.existingSecret }}
|
||||
key: secret-key
|
||||
{{- else }}
|
||||
value: {{ .Values.global.s3.secretKey }}
|
||||
{{- end }}
|
||||
- name: REDIS_URL
|
||||
value: {{ .Values.global.redis.redisHost }}
|
||||
value: {{ default .Values.global.redis.redisHost .Values.redisHost }}
|
||||
{{- if .Values.global.enterpriseEditionLicense }}
|
||||
- name: COMPRESSION
|
||||
value: "true"
|
||||
- name: port
|
||||
value: "9000"
|
||||
- name: CACHE_REFRESH_INTERVAL_SECONDS
|
||||
value: '5'
|
||||
- name: debug
|
||||
value: '0'
|
||||
{{- end}}
|
||||
{{- range $key, $val := .Values.global.env }}
|
||||
- name: {{ $key }}
|
||||
value: '{{ $val }}'
|
||||
|
|
|
|||
|
|
@ -10,25 +10,6 @@ metadata:
|
|||
{{- include "assist.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$1
|
||||
nginx.ingress.kubernetes.io/configuration-snippet: |
|
||||
#set $sticky_used "no";
|
||||
#if ($sessionid != "") {
|
||||
# set $sticky_used "yes";
|
||||
#}
|
||||
|
||||
#add_header X-Debug-Session-ID $sessionid;
|
||||
#add_header X-Debug-Session-Type "wss";
|
||||
#add_header X-Sticky-Session-Used $sticky_used;
|
||||
#add_header X-Upstream-Server $upstream_addr;
|
||||
|
||||
proxy_hide_header access-control-allow-headers;
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
add_header 'Access-Control-Allow-Origin' $http_origin always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'sessionid, Content-Type, Authorization' always;
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
|
||||
nginx.ingress.kubernetes.io/upstream-hash-by: $sessionid
|
||||
|
||||
{{- with .Values.ingress.annotations }}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ spec:
|
|||
- name: sourcemaps_reader
|
||||
value: "http://sourcemapreader-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9000/%s/sourcemaps"
|
||||
- name: ASSIST_URL
|
||||
value: "http://assist-openreplay.{{.Release.Namespace}}.{{.Values.global.clusterDomain}}:9001/assist/%s"
|
||||
value: {{ include "openreplay.assist_url" . }}
|
||||
- name: ASSIST_JWT_SECRET
|
||||
value: {{ .Values.global.assistJWTSecret }}
|
||||
- name: JWT_SECRET
|
||||
|
|
|
|||
|
|
@ -161,3 +161,11 @@ Create the volume mount config for redis TLS certificates
|
|||
{{- printf "postgres://%s:$(pg_password)@%s:%s/%s" .Values.global.postgresql.postgresqlUser .Values.global.postgresql.postgresqlHost .Values.global.postgresql.postgresqlPort .Values.global.postgresql.postgresqlDatabase -}}
|
||||
{{- end -}}
|
||||
{{- end}}
|
||||
|
||||
{{- define "openreplay.assist_url"}}
|
||||
{{- if .Values.global.enterpriseEditionLicense }}
|
||||
{{- printf "http://assist-api-openreplay.%s.%s:9001/assist/%%s" .Release.Namespace .Values.global.clusterDomain }}
|
||||
{{- else}}
|
||||
{{- printf "http://assist-openreplay.%s.%s:9001/assist/%%s" .Release.Namespace .Values.global.clusterDomain }}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ vault: &vault
|
|||
{{- with secret "database/creds/db-app" -}}
|
||||
POSTGRES_STRING=postgres://{{.Data.username}}:{{.Data.password}}@postgresql.db.svc.cluster.local:5432/postgres
|
||||
{{- end -}}
|
||||
assist-api:
|
||||
enabled: false
|
||||
|
||||
minio:
|
||||
# Force initialize minio, even if the instance is not provisioned by OR
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue