mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 07:48:37 +01:00
fix dates
This commit is contained in:
@@ -134,11 +134,11 @@ const pricingDrawer = usePricingDrawer();
|
|||||||
}">
|
}">
|
||||||
<div class="py-4 px-2 gap-6 flex flex-col w-full">
|
<div class="py-4 px-2 gap-6 flex flex-col w-full">
|
||||||
|
|
||||||
<div class="flex px-2" v-if="!isPremium">
|
<!-- <div class="flex px-2" v-if="!isPremium">
|
||||||
<LyxUiButton type="primary" class="w-full text-center text-[.8rem] font-medium" @click="pricingDrawer.visible.value = true;">
|
<LyxUiButton type="primary" class="w-full text-center text-[.8rem] font-medium" @click="pricingDrawer.visible.value = true;">
|
||||||
Upgrade plan
|
Upgrade plan
|
||||||
</LyxUiButton>
|
</LyxUiButton>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
|
|
||||||
<div class="flex px-2 flex-col">
|
<div class="flex px-2 flex-col">
|
||||||
|
|||||||
@@ -106,11 +106,12 @@ const externalTooltipElement = ref<null | HTMLDivElement>(null);
|
|||||||
function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) {
|
function externalTooltipHandler(context: { chart: any, tooltip: TooltipModel<'line' | 'bar'> }) {
|
||||||
const { chart, tooltip } = context;
|
const { chart, tooltip } = context;
|
||||||
const tooltipEl = externalTooltipElement.value;
|
const tooltipEl = externalTooltipElement.value;
|
||||||
|
|
||||||
currentTooltipData.value = [0,0,0,''];
|
currentTooltipData.value.visits = (tooltip.dataPoints.find(e=> e.datasetIndex == 0)?.raw) as number;
|
||||||
currentTooltipData.value.push(...tooltip.dataPoints.map(e => e.raw) as number[]);
|
currentTooltipData.value.sessions = (tooltip.dataPoints.find(e=> e.datasetIndex == 1)?.raw) as number;
|
||||||
currentTooltipData.value[2] = ((tooltip.dataPoints[2]?.raw as any)?.r2 || 0) as number;
|
currentTooltipData.value.events = ((tooltip.dataPoints.find(e=> e.datasetIndex == 2)?.raw) as any)?.r2 as number;
|
||||||
currentTooltipData.value[3] = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
|
|
||||||
|
currentTooltipData.value.date = new Date(allDatesFull.value[tooltip.dataPoints[0].dataIndex]).toLocaleDateString();
|
||||||
|
|
||||||
if (!tooltipEl) return;
|
if (!tooltipEl) return;
|
||||||
if (tooltip.opacity === 0) {
|
if (tooltip.opacity === 0) {
|
||||||
@@ -147,7 +148,6 @@ function transformResponse(input: { _id: string, count: number }[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = computed(() => {
|
const body = computed(() => {
|
||||||
console.log('INDEX IS', selectedLabelIndex.value, 'VALUE IS', selectLabels[selectedLabelIndex.value].value)
|
|
||||||
return {
|
return {
|
||||||
from: safeSnapshotDates.value.from,
|
from: safeSnapshotDates.value.from,
|
||||||
to: safeSnapshotDates.value.to,
|
to: safeSnapshotDates.value.to,
|
||||||
@@ -214,7 +214,8 @@ function onDataReady() {
|
|||||||
chartData.value.datasets[0].data = visitsData.data.value.data;
|
chartData.value.datasets[0].data = visitsData.data.value.data;
|
||||||
chartData.value.datasets[1].data = sessionsData.data.value.data;
|
chartData.value.datasets[1].data = sessionsData.data.value.data;
|
||||||
chartData.value.datasets[2].data = eventsData.data.value.data.map(e => {
|
chartData.value.datasets[2].data = eventsData.data.value.data.map(e => {
|
||||||
return { x: 0, y: maxChartY + 70, r: 25 / maxEventSize * e, r2: e }
|
const rValue = 25 / maxEventSize * e;
|
||||||
|
return { x: 0, y: maxChartY + 70, r: isNaN(rValue) ? 0 : rValue, r2: e }
|
||||||
});
|
});
|
||||||
|
|
||||||
chartData.value.datasets[0].backgroundColor = [createGradient('#5655d7')];
|
chartData.value.datasets[0].backgroundColor = [createGradient('#5655d7')];
|
||||||
@@ -226,7 +227,14 @@ function onDataReady() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTooltipData = ref<[number, number, number, string]>([0, 0, 0, '']);
|
const currentTooltipData = ref<{ visits: number, events: number, sessions: number, date: string }>({
|
||||||
|
visits: 0,
|
||||||
|
events: 0,
|
||||||
|
sessions: 0,
|
||||||
|
date: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const tooltipNameIndex = ['visits', 'sessions', 'events'];
|
||||||
|
|
||||||
function onLegendChange(dataset: any, index: number, checked: any) {
|
function onLegendChange(dataset: any, index: number, checked: any) {
|
||||||
dataset.hidden = !checked;
|
dataset.hidden = !checked;
|
||||||
@@ -273,14 +281,14 @@ onMounted(async () => {
|
|||||||
<LyxUiCard>
|
<LyxUiCard>
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center">
|
||||||
<div> Date: </div>
|
<div> Date: </div>
|
||||||
<div v-if="currentTooltipData"> {{ currentTooltipData[3] }}</div>
|
<div v-if="currentTooltipData"> {{ currentTooltipData.date }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="(dataset, index) of chartData.datasets" class="flex gap-2 items-center">
|
<div v-for="(dataset, index) of chartData.datasets" class="flex gap-2 items-center">
|
||||||
<div :style="`background-color: ${legendColors[index]}`" class="h-4 w-4 rounded-full">
|
<div :style="`background-color: ${legendColors[index]}`" class="h-4 w-4 rounded-full">
|
||||||
</div>
|
</div>
|
||||||
<div> {{ dataset.label }}</div>
|
<div> {{ dataset.label }}</div>
|
||||||
<div v-if="currentTooltipData" class="grow text-right px-4">
|
<div v-if="currentTooltipData" class="grow text-right px-4">
|
||||||
{{ currentTooltipData[index] }}
|
{{ (currentTooltipData as any)[tooltipNameIndex[index]] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> -->
|
<!-- <div class="bg-lyx-background-lighter h-[2px] w-full my-2"> </div> -->
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ export default defineNuxtConfig({
|
|||||||
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
|
AUTH_JWT_SECRET: process.env.AUTH_JWT_SECRET,
|
||||||
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
|
GOOGLE_AUTH_CLIENT_ID: process.env.GOOGLE_AUTH_CLIENT_ID,
|
||||||
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,
|
GOOGLE_AUTH_CLIENT_SECRET: process.env.GOOGLE_AUTH_CLIENT_SECRET,
|
||||||
|
GITHUB_AUTH_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID,
|
||||||
|
GITHUB_AUTH_CLIENT_SECRET: process.env.GITHUB_AUTH_CLIENT_SECRET,
|
||||||
STRIPE_SECRET: process.env.STRIPE_SECRET,
|
STRIPE_SECRET: process.env.STRIPE_SECRET,
|
||||||
STRIPE_WH_SECRET: process.env.STRIPE_WH_SECRET,
|
STRIPE_WH_SECRET: process.env.STRIPE_WH_SECRET,
|
||||||
STRIPE_SECRET_TEST: process.env.STRIPE_SECRET_TEST,
|
STRIPE_SECRET_TEST: process.env.STRIPE_SECRET_TEST,
|
||||||
@@ -50,7 +52,8 @@ export default defineNuxtConfig({
|
|||||||
NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL,
|
NOAUTH_USER_EMAIL: process.env.NOAUTH_USER_EMAIL,
|
||||||
NOAUTH_USER_NAME: process.env.NOAUTH_USER_NAME,
|
NOAUTH_USER_NAME: process.env.NOAUTH_USER_NAME,
|
||||||
public: {
|
public: {
|
||||||
AUTH_MODE: process.env.AUTH_MODE
|
AUTH_MODE: process.env.AUTH_MODE,
|
||||||
|
GITHUB_CLIENT_ID: process.env.GITHUB_AUTH_CLIENT_ID || 'NONE'
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ const { snapshot } = useSnapshot();
|
|||||||
|
|
||||||
const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProject.value?._id.toString()}`);
|
const refreshKey = computed(() => `${snapshot.value._id.toString() + activeProject.value?._id.toString()}`);
|
||||||
|
|
||||||
|
const isPremium = computed(() => {
|
||||||
|
return activeProject.value?.premium;
|
||||||
|
})
|
||||||
|
|
||||||
const pricingDrawer = usePricingDrawer();
|
const pricingDrawer = usePricingDrawer();
|
||||||
|
|
||||||
function goToUpgrade() {
|
function goToUpgrade() {
|
||||||
@@ -93,9 +97,7 @@ function goToUpgrade() {
|
|||||||
|
|
||||||
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction.data.value">
|
<div :key="'home-' + isLiveDemo()" v-if="projects && activeProject && firstInteraction.data.value">
|
||||||
|
|
||||||
<div class="w-full px-4 py-2">
|
<div class="w-full px-4 py-2 gap-2 flex flex-col">
|
||||||
|
|
||||||
|
|
||||||
<div v-if="limitsInfo && limitsInfo.limited"
|
<div v-if="limitsInfo && limitsInfo.limited"
|
||||||
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
||||||
<div class="flex flex-col grow">
|
<div class="flex flex-col grow">
|
||||||
@@ -110,23 +112,38 @@ function goToUpgrade() {
|
|||||||
<div>
|
<div>
|
||||||
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="!isPremium" class="w-full bg-[#5680f822] p-4 rounded-lg text-[.9rem] flex items-center">
|
||||||
|
<div class="flex flex-col grow">
|
||||||
|
<div class="poppins font-semibold text-lyx-primary">
|
||||||
|
Launch offer: 25% off
|
||||||
|
</div>
|
||||||
|
<div class="poppins text-lyx-primary">
|
||||||
|
We're offering an exclusive 25% discount forever on all plans starting from the Acceleration
|
||||||
|
Plan for our first 100 users who believe in our project.
|
||||||
|
<br>
|
||||||
|
Redeem Code: <span class="text-white font-bold text-[1rem]">LIT25</span> at checkout to
|
||||||
|
claim your discount.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <DashboardTopSection></DashboardTopSection>
|
<DashboardTopSection></DashboardTopSection>
|
||||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards> -->
|
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
||||||
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row">
|
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row">
|
||||||
|
|
||||||
<!-- <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Visits trends"
|
<CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Visits trends"
|
||||||
sub="Shows trends in page visits.">
|
sub="Shows trends in page visits.">
|
||||||
<template #header>
|
<template #header>
|
||||||
<SelectButton @changeIndex="mainChartSelectIndex = $event" :currentIndex="mainChartSelectIndex"
|
<SelectButton @changeIndex="mainChartSelectIndex = $event" :currentIndex="mainChartSelectIndex"
|
||||||
@@ -137,24 +154,23 @@ function goToUpgrade() {
|
|||||||
<DashboardVisitsLineChart :slice="(selectLabels[mainChartSelectIndex].value as any)">
|
<DashboardVisitsLineChart :slice="(selectLabels[mainChartSelectIndex].value as any)">
|
||||||
</DashboardVisitsLineChart>
|
</DashboardVisitsLineChart>
|
||||||
</div>
|
</div>
|
||||||
</CardTitled> -->
|
</CardTitled>
|
||||||
|
|
||||||
<!-- <CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions"
|
<CardTitled :key="refreshKey" class="p-4 flex-1 w-full" title="Sessions" sub="Shows trends in sessions.">
|
||||||
sub="Shows trends in sessions.">
|
<template #header>
|
||||||
<template #header>
|
|
||||||
<SelectButton @changeIndex="sessionsChartSelectIndex = $event"
|
<SelectButton @changeIndex="sessionsChartSelectIndex = $event"
|
||||||
:currentIndex="sessionsChartSelectIndex" :options="selectLabels">
|
:currentIndex="sessionsChartSelectIndex" :options="selectLabels">
|
||||||
</SelectButton>
|
</SelectButton>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
<div>
|
||||||
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
|
<DashboardSessionsLineChart :slice="(selectLabels[sessionsChartSelectIndex].value as any)">
|
||||||
</DashboardSessionsLineChart>
|
</DashboardSessionsLineChart>
|
||||||
</div>
|
</div>
|
||||||
</CardTitled> -->
|
</CardTitled>
|
||||||
|
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- <div class="flex w-full justify-center mt-6 px-6">
|
<div class="flex w-full justify-center mt-6 px-6">
|
||||||
<div class="flex w-full gap-6 flex-col xl:flex-row">
|
<div class="flex w-full gap-6 flex-col xl:flex-row">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard>
|
<DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard>
|
||||||
@@ -195,7 +211,7 @@ function goToUpgrade() {
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,33 @@ function handleOnError(errorResponse: any) {
|
|||||||
alert('Error' + errorResponse);
|
alert('Error' + errorResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getRandomHex(size: number) {
|
||||||
|
const bytes = new Uint8Array(size);
|
||||||
|
window.crypto.getRandomValues(bytes);
|
||||||
|
return Array.from(bytes)
|
||||||
|
.map((byte) => byte.toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function githubLogin() {
|
||||||
|
const client_id = config.public.GITHUB_CLIENT_ID;
|
||||||
|
const redirect_uri = window.location.origin + '/api';
|
||||||
|
console.log({ redirect_uri })
|
||||||
|
const state = getRandomHex(16);
|
||||||
|
localStorage.setItem("latestCSRFToken", state);
|
||||||
|
const link = `https://github.com/login/oauth/authorize?client_id=${client_id}&response_type=code&scope=repo&redirect_uri=${redirect_uri}/integrations/github/oauth2/callback&state=${state}`;
|
||||||
|
window.location.assign(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.github_access_token) {
|
||||||
|
//TODO: Something
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@@ -103,23 +130,34 @@ function handleOnError(errorResponse: any) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2">
|
<div class="text-text/80 text-[1.2rem] text-center w-[70%] poppins mt-2">
|
||||||
Real-time analytics for 15+ JS/TS frameworks
|
Track web analytics and custom events
|
||||||
<br>
|
<br>
|
||||||
with one-line code setup.
|
with extreme simplicity in under 30 sec.
|
||||||
<br>
|
<br>
|
||||||
<div class="font-bold poppins mt-4">
|
<!-- <div class="font-bold poppins mt-4">
|
||||||
Start for Free now! Up to 3k visits/events monthly.
|
Start for Free now! Up to 3k visits/events monthly.
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-12">
|
<div class="mt-12">
|
||||||
|
|
||||||
<div v-if="!isNoAuth" @click="login"
|
|
||||||
class="hover:bg-accent cursor-pointer flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]">
|
<div v-if="!isNoAuth" class="flex flex-col gap-2">
|
||||||
<div class="flex items-center">
|
<div @click="login"
|
||||||
<i class="fab fa-google"></i>
|
class="hover:bg-accent cursor-pointer flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fab fa-google"></i>
|
||||||
|
</div>
|
||||||
|
Continue with Google
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class=" opacity-35 cursor-not-allowed flex text-[1.3rem] gap-4 items-center border-[1px] border-gray-400 rounded-lg px-8 py-3 relative z-[2]">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fab fa-github"></i>
|
||||||
|
</div>
|
||||||
|
Continue with GitHub
|
||||||
</div>
|
</div>
|
||||||
Continue with Google
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isNoAuth" @click="loginWithoutAuth"
|
<div v-if="isNoAuth" @click="loginWithoutAuth"
|
||||||
@@ -133,7 +171,7 @@ function handleOnError(errorResponse: any) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-[.9rem] poppins mt-12 text-text-sub text-center relative z-[2]">
|
<div class="text-[.9rem] poppins mt-12 text-text-sub text-center relative z-[2]">
|
||||||
By continuing you are indicating that you accept
|
By continuing you are accepting
|
||||||
<br>
|
<br>
|
||||||
our
|
our
|
||||||
<a class="underline" href="https://litlyx.com/terms" target="_blank">Terms of Service</a> and
|
<a class="underline" href="https://litlyx.com/terms" target="_blank">Terms of Service</a> and
|
||||||
|
|||||||
72
dashboard/server/api/integrations/github/oauth2/callback.ts
Normal file
72
dashboard/server/api/integrations/github/oauth2/callback.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
import { createUserJwt } from '~/server/AuthManager';
|
||||||
|
import { UserModel } from '@schema/UserSchema';
|
||||||
|
import EmailService from '@services/EmailService';
|
||||||
|
|
||||||
|
const config = useRuntimeConfig();
|
||||||
|
|
||||||
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
|
const { code } = getQuery(event);
|
||||||
|
console.log('CODE', code);
|
||||||
|
|
||||||
|
const redirect_uri = 'http://127.0.0.1:3000'
|
||||||
|
|
||||||
|
const res = await fetch(`https://github.com/login/oauth/access_token?client_id=${config.GITHUB_AUTH_CLIENT_ID}&client_secret=${config.GITHUB_AUTH_CLIENT_SECRET}&code=${code}&redirect_url=${redirect_uri}`, {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
const access_token = data.access_token;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
return sendRedirect(event,`http://127.0.0.1:3000/login?github_access_token=${access_token}`)
|
||||||
|
|
||||||
|
|
||||||
|
// const origin = event.headers.get('origin');
|
||||||
|
|
||||||
|
// const tokenResponse = await client.getToken({
|
||||||
|
// code: body.code,
|
||||||
|
// redirect_uri: origin || ''
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const tokens = tokenResponse.tokens;
|
||||||
|
|
||||||
|
// const ticket = await client.verifyIdToken({
|
||||||
|
// idToken: tokens.id_token || '',
|
||||||
|
// audience: GOOGLE_AUTH_CLIENT_ID,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const payload = ticket.getPayload();
|
||||||
|
// if (!payload) return { error: true, access_token: '' };
|
||||||
|
|
||||||
|
|
||||||
|
// const user = await UserModel.findOne({ email: payload.email });
|
||||||
|
|
||||||
|
// if (user) return { error: false, access_token: createUserJwt({ email: user.email, name: user.name }) }
|
||||||
|
|
||||||
|
|
||||||
|
// const newUser = new UserModel({
|
||||||
|
// email: payload.email,
|
||||||
|
// given_name: payload.given_name,
|
||||||
|
// name: payload.name,
|
||||||
|
// locale: payload.locale,
|
||||||
|
// picture: payload.picture,
|
||||||
|
// created_at: Date.now()
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const savedUser = await newUser.save();
|
||||||
|
|
||||||
|
// setImmediate(() => {
|
||||||
|
// console.log('SENDING WELCOME EMAIL TO', payload.email);
|
||||||
|
// if (payload.email) EmailService.sendWelcomeEmail(payload.email);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return { error: false, access_token: createUserJwt({ email: savedUser.email, name: savedUser.name }) }
|
||||||
|
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@ import { EventModel } from "@schema/metrics/EventSchema";
|
|||||||
import { getTimeline } from "./generic";
|
import { getTimeline } from "./generic";
|
||||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
import { executeTimelineAggregation, fillAndMergeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const project_id = getRequestProjectId(event);
|
const project_id = getRequestProjectId(event);
|
||||||
@@ -27,7 +27,7 @@ export default defineEventHandler(async event => {
|
|||||||
model: EventModel,
|
model: EventModel,
|
||||||
from, to, slice
|
from, to, slice
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||||
return timelineFilledMerged;
|
return timelineFilledMerged;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { getTimeline } from "./generic";
|
|
||||||
import { VisitModel } from "@schema/metrics/VisitSchema";
|
import { VisitModel } from "@schema/metrics/VisitSchema";
|
||||||
import DateService from "@services/DateService";
|
|
||||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const project_id = getRequestProjectId(event);
|
const project_id = getRequestProjectId(event);
|
||||||
@@ -31,7 +29,7 @@ export default defineEventHandler(async event => {
|
|||||||
referrer
|
referrer
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||||
return timelineFilledMerged;
|
return timelineFilledMerged;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
|
|||||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import { SessionModel } from "@schema/metrics/SessionSchema";
|
import { SessionModel } from "@schema/metrics/SessionSchema";
|
||||||
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const project_id = getRequestProjectId(event);
|
const project_id = getRequestProjectId(event);
|
||||||
@@ -28,7 +28,7 @@ export default defineEventHandler(async event => {
|
|||||||
model: SessionModel,
|
model: SessionModel,
|
||||||
from, to, slice
|
from, to, slice
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||||
return timelineFilledMerged;
|
return timelineFilledMerged;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getTimeline } from "./generic";
|
|||||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import { SessionModel } from "@schema/metrics/SessionSchema";
|
import { SessionModel } from "@schema/metrics/SessionSchema";
|
||||||
import { executeAdvancedTimelineAggregation, executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
import { executeAdvancedTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const project_id = getRequestProjectId(event);
|
const project_id = getRequestProjectId(event);
|
||||||
@@ -45,7 +45,7 @@ export default defineEventHandler(async event => {
|
|||||||
count: { $divide: ["$duration", "$count"] }
|
count: { $divide: ["$duration", "$count"] }
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from ,to);
|
||||||
return timelineFilledMerged;
|
return timelineFilledMerged;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { VisitModel } from "@schema/metrics/VisitSchema";
|
|||||||
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
import { Redis, TIMELINE_EXPIRE_TIME } from "~/server/services/CacheService";
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
||||||
import DateService from "@services/DateService";
|
import DateService from "@services/DateService";
|
||||||
import { executeTimelineAggregation, fillAndMergeTimelineAggregation } from "~/server/services/TimelineService";
|
import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
const project_id = getRequestProjectId(event);
|
const project_id = getRequestProjectId(event);
|
||||||
@@ -28,11 +28,9 @@ export default defineEventHandler(async event => {
|
|||||||
model: VisitModel,
|
model: VisitModel,
|
||||||
from, to, slice,
|
from, to, slice,
|
||||||
});
|
});
|
||||||
const timelineFilledMerged = fillAndMergeTimelineAggregation(timelineData, slice);
|
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, slice, from, to);
|
||||||
return timelineFilledMerged;
|
return timelineFilledMerged;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -61,4 +61,10 @@ export function fillAndMergeTimelineAggregation(timeline: { _id: string, count:
|
|||||||
const filledDates = DateService.fillDates(timeline.map(e => e._id), slice);
|
const filledDates = DateService.fillDates(timeline.map(e => e._id), slice);
|
||||||
const merged = DateService.mergeFilledDates(filledDates, timeline, '_id', slice, { count: 0 });
|
const merged = DateService.mergeFilledDates(filledDates, timeline, '_id', slice, { count: 0 });
|
||||||
return merged;
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fillAndMergeTimelineAggregationV2(timeline: { _id: string, count: number }[], slice: Slice, from: string, to: string) {
|
||||||
|
const filledDates = DateService.createBetweenDates(from, to, slice);
|
||||||
|
const merged = DateService.mergeFilledDates(filledDates.dates, timeline, '_id', slice, { count: 0 });
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
@@ -102,6 +102,18 @@ class DateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createBetweenDates(from: string, to: string, slice: Slice) {
|
||||||
|
let start = dayjs(from);
|
||||||
|
const end = dayjs(to);
|
||||||
|
const filledDates: dayjs.Dayjs[] = [];
|
||||||
|
while (start.isBefore(end) || start.isSame(end)) {
|
||||||
|
filledDates.push(start);
|
||||||
|
start = start.add(1, slice);
|
||||||
|
}
|
||||||
|
return { dates: filledDates, from, to };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fillDates(dates: string[], slice: Slice) {
|
fillDates(dates: string[], slice: Slice) {
|
||||||
const allDates: dayjs.Dayjs[] = [];
|
const allDates: dayjs.Dayjs[] = [];
|
||||||
const firstDate = dayjs(dates.at(0));
|
const firstDate = dayjs(dates.at(0));
|
||||||
@@ -109,7 +121,7 @@ class DateService {
|
|||||||
let currentDate = firstDate.clone();
|
let currentDate = firstDate.clone();
|
||||||
|
|
||||||
allDates.push(currentDate);
|
allDates.push(currentDate);
|
||||||
|
|
||||||
while (currentDate.isBefore(lastDate, slice)) {
|
while (currentDate.isBefore(lastDate, slice)) {
|
||||||
currentDate = currentDate.add(1, slice);
|
currentDate = currentDate.add(1, slice);
|
||||||
allDates.push(currentDate);
|
allDates.push(currentDate);
|
||||||
@@ -121,7 +133,7 @@ class DateService {
|
|||||||
mergeFilledDates<T extends Record<string, any>, K extends keyof T>(dates: dayjs.Dayjs[], items: T[], dateField: K, slice: Slice, fillData: Omit<T, K>) {
|
mergeFilledDates<T extends Record<string, any>, K extends keyof T>(dates: dayjs.Dayjs[], items: T[], dateField: K, slice: Slice, fillData: Omit<T, K>) {
|
||||||
const result = new Array<T>();
|
const result = new Array<T>();
|
||||||
for (const date of dates) {
|
for (const date of dates) {
|
||||||
const item = items.find(e => dayjs(e[dateField]).isSame(date), slice);
|
const item = items.find(e => dayjs(e[dateField]).isSame(date, slice));
|
||||||
result.push(item ?? { ...fillData, [dateField]: date.format() } as T);
|
result.push(item ?? { ...fillData, [dateField]: date.format() } as T);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user