mirror of
https://github.com/Litlyx/litlyx
synced 2025-12-10 15:58:38 +01:00
rewrite settings + banners
This commit is contained in:
3
TODO
3
TODO
@@ -2,3 +2,6 @@
|
|||||||
|
|
||||||
- Slice change on Actionable Chart
|
- Slice change on Actionable Chart
|
||||||
- Show more on Dashboard cards
|
- Show more on Dashboard cards
|
||||||
|
- Fix devices Dashboard card, replace empty with "unkwnwn"
|
||||||
|
- Component first interaction must make the request inside
|
||||||
|
- Reactivity on project delete
|
||||||
@@ -24,8 +24,8 @@ function showMore() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<BarCardBase @showMore="showMore()" @dataReload="devicesData.refresh()" :data="devicesData.data.value || []" :dataIcons="false"
|
<BarCardBase @showMore="showMore()" @dataReload="devicesData.refresh()" :data="devicesData.data.value || []"
|
||||||
desc="The devices most used to access your website." :loading="devicesData.pending.value" label="Top Devices"
|
:dataIcons="false" desc="The devices most used to access your website." :loading="devicesData.pending.value"
|
||||||
sub-label="Devices"></BarCardBase>
|
label="Top Devices" sub-label="Devices"></BarCardBase>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
33
dashboard/components/banner/LimitsInfo.vue
Normal file
33
dashboard/components/banner/LimitsInfo.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
|
||||||
|
const limitsInfo = await useFetch("/api/project/limits_info", {
|
||||||
|
lazy: true, headers: useComputedHeaders({ useSnapshotDates: false })
|
||||||
|
});
|
||||||
|
|
||||||
|
const pricingDrawer = usePricingDrawer();
|
||||||
|
|
||||||
|
function goToUpgrade() {
|
||||||
|
pricingDrawer.visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="limitsInfo.data.value && limitsInfo.data.value.limited"
|
||||||
|
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
||||||
|
<div class="flex flex-col grow">
|
||||||
|
<div class="poppins font-semibold text-[#fbbf24]">
|
||||||
|
Limit reached
|
||||||
|
</div>
|
||||||
|
<div class="poppins text-[#fbbf24]">
|
||||||
|
Litlyx cannot receive new data as you reached your plan's limit. Resume all the great
|
||||||
|
features and collect even more data with a higher plan.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
37
dashboard/components/banner/Offer.vue
Normal file
37
dashboard/components/banner/Offer.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
|
||||||
|
|
||||||
|
const pricingDrawer = usePricingDrawer();
|
||||||
|
|
||||||
|
function goToUpgrade() {
|
||||||
|
pricingDrawer.visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { project } = useProject()
|
||||||
|
|
||||||
|
const isPremium = computed(() => {
|
||||||
|
return project.value?.premium ?? false;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import type { TApiSettings } from '@schema/ApiSettingsSchema';
|
import type { TApiSettings } from '@schema/ApiSettingsSchema';
|
||||||
import type { SettingsTemplateEntry } from './Template.vue';
|
import type { SettingsTemplateEntry } from './Template.vue';
|
||||||
|
|
||||||
|
const { project } = useProject();
|
||||||
|
|
||||||
const entries: SettingsTemplateEntry[] = [
|
const entries: SettingsTemplateEntry[] = [
|
||||||
{ id: 'pname', title: 'Name', text: 'Project name' },
|
{ id: 'pname', title: 'Name', text: 'Project name' },
|
||||||
@@ -11,8 +12,7 @@ const entries: SettingsTemplateEntry[] = [
|
|||||||
{ id: 'pdelete', title: 'Delete', text: 'Delete current project' },
|
{ id: 'pdelete', title: 'Delete', text: 'Delete current project' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const activeProject = useActiveProject();
|
const projectNameInputVal = ref<string>(project.value?.name || '');
|
||||||
const projectNameInputVal = ref<string>(activeProject.value?.name || '');
|
|
||||||
|
|
||||||
const apiKeys = ref<TApiSettings[]>([]);
|
const apiKeys = ref<TApiSettings[]>([]);
|
||||||
|
|
||||||
@@ -20,14 +20,17 @@ const newApiKeyName = ref<string>('');
|
|||||||
|
|
||||||
async function updateApiKeys() {
|
async function updateApiKeys() {
|
||||||
newApiKeyName.value = '';
|
newApiKeyName.value = '';
|
||||||
apiKeys.value = await $fetch<TApiSettings[]>('/api/keys/get_all', signHeaders());
|
apiKeys.value = await $fetch<TApiSettings[]>('/api/keys/get_all', signHeaders({
|
||||||
|
'x-pid': project.value?._id.toString() ?? ''
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createApiKey() {
|
async function createApiKey() {
|
||||||
try {
|
try {
|
||||||
const res = await $fetch<TApiSettings>('/api/keys/create', {
|
const res = await $fetch<TApiSettings>('/api/keys/create', {
|
||||||
method: 'POST', ...signHeaders({
|
method: 'POST', ...signHeaders({
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'x-pid': project.value?._id.toString() ?? ''
|
||||||
}),
|
}),
|
||||||
body: JSON.stringify({ name: newApiKeyName.value })
|
body: JSON.stringify({ name: newApiKeyName.value })
|
||||||
});
|
});
|
||||||
@@ -42,7 +45,8 @@ async function deleteApiKey(api_id: string) {
|
|||||||
try {
|
try {
|
||||||
const res = await $fetch<TApiSettings>('/api/keys/delete', {
|
const res = await $fetch<TApiSettings>('/api/keys/delete', {
|
||||||
method: 'DELETE', ...signHeaders({
|
method: 'DELETE', ...signHeaders({
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
|
'x-pid': project.value?._id.toString() ?? ''
|
||||||
}),
|
}),
|
||||||
body: JSON.stringify({ api_id })
|
body: JSON.stringify({ api_id })
|
||||||
});
|
});
|
||||||
@@ -56,15 +60,15 @@ async function deleteApiKey(api_id: string) {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateApiKeys();
|
updateApiKeys();
|
||||||
})
|
});
|
||||||
|
|
||||||
watch(activeProject, () => {
|
watch(project, () => {
|
||||||
projectNameInputVal.value = activeProject.value?.name || "";
|
projectNameInputVal.value = project.value?.name || "";
|
||||||
updateApiKeys();
|
updateApiKeys();
|
||||||
})
|
});
|
||||||
|
|
||||||
const canChange = computed(() => {
|
const canChange = computed(() => {
|
||||||
if (activeProject.value?.name == projectNameInputVal.value) return false;
|
if (project.value?.name == projectNameInputVal.value) return false;
|
||||||
if (projectNameInputVal.value.length === 0) return false;
|
if (projectNameInputVal.value.length === 0) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -80,8 +84,8 @@ async function changeProjectName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deleteProject() {
|
async function deleteProject() {
|
||||||
if (!activeProject.value) return;
|
if (!project.value) return;
|
||||||
const sure = confirm(`Are you sure to delete the project ${activeProject.value.name} ?`);
|
const sure = confirm(`Are you sure to delete the project ${project.value.name} ?`);
|
||||||
if (!sure) return;
|
if (!sure) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -89,7 +93,7 @@ async function deleteProject() {
|
|||||||
await $fetch('/api/project/delete', {
|
await $fetch('/api/project/delete', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
...signHeaders({ 'Content-Type': 'application/json' }),
|
...signHeaders({ 'Content-Type': 'application/json' }),
|
||||||
body: JSON.stringify({ project_id: activeProject.value._id.toString() })
|
body: JSON.stringify({ project_id: project.value._id.toString() })
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectsList = useProjectsList()
|
const projectsList = useProjectsList()
|
||||||
@@ -142,7 +146,7 @@ function copyProjectId() {
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SettingsTemplate :entries="entries" :key="activeProject?.name || 'NONE'">
|
<SettingsTemplate :entries="entries" :key="project?.name || 'NONE'">
|
||||||
<template #pname>
|
<template #pname>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<LyxUiInput class="w-full px-4 py-2" v-model="projectNameInputVal"></LyxUiInput>
|
<LyxUiInput class="w-full px-4 py-2" v-model="projectNameInputVal"></LyxUiInput>
|
||||||
@@ -174,7 +178,7 @@ function copyProjectId() {
|
|||||||
</template>
|
</template>
|
||||||
<template #pid>
|
<template #pid>
|
||||||
<LyxUiCard class="w-full flex items-center">
|
<LyxUiCard class="w-full flex items-center">
|
||||||
<div class="grow">{{ activeProject?._id.toString() }}</div>
|
<div class="grow">{{ project?._id.toString() }}</div>
|
||||||
<div><i class="far fa-copy" @click="copyProjectId()"></i></div>
|
<div><i class="far fa-copy" @click="copyProjectId()"></i></div>
|
||||||
</LyxUiCard>
|
</LyxUiCard>
|
||||||
</template>
|
</template>
|
||||||
@@ -182,7 +186,7 @@ function copyProjectId() {
|
|||||||
<LyxUiCard class="w-full flex items-center">
|
<LyxUiCard class="w-full flex items-center">
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
{{ `
|
{{ `
|
||||||
<script defer data-project="${activeProject?._id}"
|
<script defer data-project="${project?._id}"
|
||||||
src="https://cdn.jsdelivr.net/gh/litlyx/litlyx-js/browser/litlyx.js"></script>` }}
|
src="https://cdn.jsdelivr.net/gh/litlyx/litlyx-js/browser/litlyx.js"></script>` }}
|
||||||
</div>
|
</div>
|
||||||
<div><i class="far fa-copy" @click="copyScript()"></i></div>
|
<div><i class="far fa-copy" @click="copyScript()"></i></div>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const { projectId } = useProject();
|
|||||||
const { safeSnapshotDates } = useSnapshot()
|
const { safeSnapshotDates } = useSnapshot()
|
||||||
|
|
||||||
function getValueFromRefOrPrimitive<T>(data?: T | Ref<T> | ComputedRef<T>) {
|
function getValueFromRefOrPrimitive<T>(data?: T | Ref<T> | ComputedRef<T>) {
|
||||||
console.log('Getting value of', data);
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (isRef(data)) return data.value;
|
if (isRef(data)) return data.value;
|
||||||
return data;
|
return data;
|
||||||
@@ -30,7 +29,6 @@ export function useComputedHeaders(customOptions?: CustomOptions) {
|
|||||||
const parsedCustom: Record<string, string> = {}
|
const parsedCustom: Record<string, string> = {}
|
||||||
const customKeys = Object.keys(customOptions?.custom || {});
|
const customKeys = Object.keys(customOptions?.custom || {});
|
||||||
for (const key of customKeys) {
|
for (const key of customKeys) {
|
||||||
console.log('key', key);
|
|
||||||
parsedCustom[key] = getValueFromRefOrPrimitive((customOptions?.custom || {})[key]) ?? ''
|
parsedCustom[key] = getValueFromRefOrPrimitive((customOptions?.custom || {})[key]) ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,68 +3,16 @@
|
|||||||
definePageMeta({ layout: 'dashboard' });
|
definePageMeta({ layout: 'dashboard' });
|
||||||
|
|
||||||
|
|
||||||
const { project } = useProject();
|
|
||||||
|
|
||||||
|
|
||||||
const { data: projects } = useProjectsList();
|
|
||||||
const activeProject = useActiveProject();
|
|
||||||
|
|
||||||
const mainChartSelectIndex = ref<number>(1);
|
|
||||||
const sessionsChartSelectIndex = ref<number>(1);
|
|
||||||
|
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const { project, projectList } = useProject();
|
||||||
|
|
||||||
|
const justLogged = computed(() => route.query.just_logged);
|
||||||
const limitsInfo = ref<{
|
|
||||||
limited: boolean,
|
|
||||||
maxLimit: number,
|
|
||||||
limit: number,
|
|
||||||
total: number,
|
|
||||||
percent: number
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const justLogged = computed(() => {
|
|
||||||
return route.query.just_logged;
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (route.query.just_logged) return location.href = '/';
|
|
||||||
limitsInfo.value = await $fetch<any>("/api/project/limits_info", signHeaders());
|
|
||||||
watch(activeProject, async () => {
|
|
||||||
limitsInfo.value = await $fetch<any>("/api/project/limits_info", signHeaders());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const firstInteraction = useFetch<boolean>('/api/project/first_interaction', {
|
const firstInteraction = useFetch<boolean>('/api/project/first_interaction', {
|
||||||
lazy: true, headers: useComputedHeaders({ useSnapshotDates: false })
|
lazy: true, headers: useComputedHeaders({ useSnapshotDates: false })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showDashboard = computed(() => project.value && firstInteraction.data.value);
|
||||||
|
|
||||||
const showDashboard = computed(() => {
|
|
||||||
return project.value && firstInteraction.data.value
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectLabels = [
|
|
||||||
{ label: 'Hour', value: 'hour' },
|
|
||||||
{ label: 'Day', value: 'day' },
|
|
||||||
// { label: 'Month', value: 'month' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const { snapshot } = useSnapshot();
|
|
||||||
|
|
||||||
const isPremium = computed(() => {
|
|
||||||
return activeProject.value?.premium;
|
|
||||||
})
|
|
||||||
|
|
||||||
const pricingDrawer = usePricingDrawer();
|
|
||||||
|
|
||||||
function goToUpgrade() {
|
|
||||||
pricingDrawer.visible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -75,40 +23,8 @@ function goToUpgrade() {
|
|||||||
<div v-if="showDashboard">
|
<div v-if="showDashboard">
|
||||||
|
|
||||||
<div class="w-full px-4 py-2 gap-2 flex flex-col">
|
<div class="w-full px-4 py-2 gap-2 flex flex-col">
|
||||||
|
<BannerLimitsInfo :key="refreshKey"></BannerLimitsInfo>
|
||||||
<!-- <div v-if="limitsInfo && limitsInfo.limited"
|
<BannerOffer :key="refreshKey"></BannerOffer>
|
||||||
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
|
||||||
<div class="flex flex-col grow">
|
|
||||||
<div class="poppins font-semibold text-[#fbbf24]">
|
|
||||||
Limit reached
|
|
||||||
</div>
|
|
||||||
<div class="poppins text-[#fbbf24]">
|
|
||||||
Litlyx cannot receive new data as you reached your plan's limit. Resume all the great
|
|
||||||
features and collect even more data with a higher plan.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
|
||||||
</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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -165,108 +81,17 @@ function goToUpgrade() {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <div :key="'home-' + isLiveDemo()"
|
|
||||||
v-if="projects && activeProject && (firstInteraction.data.value === true) && !justLogged">
|
|
||||||
|
|
||||||
<div class="w-full px-4 py-2 gap-2 flex flex-col">
|
|
||||||
<div v-if="limitsInfo && limitsInfo.limited"
|
|
||||||
class="w-full bg-[#fbbf2422] p-4 rounded-lg text-[.9rem] flex items-center">
|
|
||||||
<div class="flex flex-col grow">
|
|
||||||
<div class="poppins font-semibold text-[#fbbf24]">
|
|
||||||
Limit reached
|
|
||||||
</div>
|
|
||||||
<div class="poppins text-[#fbbf24]">
|
|
||||||
Litlyx cannot receive new data as you reached your plan's limit. Resume all the great
|
|
||||||
features and collect even more data with a higher plan.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<LyxUiButton type="outline" @click="goToUpgrade()"> Upgrade </LyxUiButton>
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<DashboardTopSection :key="refreshKey"></DashboardTopSection>
|
|
||||||
<DashboardTopCards :key="refreshKey"></DashboardTopCards>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-6 px-6 flex gap-6 flex-col 2xl:flex-row w-full">
|
|
||||||
<DashboardActionableChart :key="refreshKey"></DashboardActionableChart>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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-1">
|
|
||||||
<DashboardWebsitesBarCard :key="refreshKey"></DashboardWebsitesBarCard>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<DashboardEventsBarCard :key="refreshKey"></DashboardEventsBarCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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-1">
|
|
||||||
<DashboardReferrersBarCard :key="refreshKey"></DashboardReferrersBarCard>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<DashboardBrowsersBarCard :key="refreshKey"></DashboardBrowsersBarCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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-1">
|
|
||||||
<DashboardOssBarCard :key="refreshKey"></DashboardOssBarCard>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<DashboardGeolocationBarCard :key="refreshKey"></DashboardGeolocationBarCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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-1">
|
|
||||||
<DashboardDevicesBarCard :key="refreshKey"></DashboardDevicesBarCard>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FirstInteraction v-if="!justLogged" :refresh-interaction="firstInteraction.refresh"
|
<FirstInteraction v-if="!justLogged" :refresh-interaction="firstInteraction.refresh"
|
||||||
:first-interaction="(firstInteraction.data.value || false)"></FirstInteraction>
|
:first-interaction="(firstInteraction.data.value || false)"></FirstInteraction>
|
||||||
|
|
||||||
<div class="text-text/85 mt-8 ml-8 poppis text-[1.2rem]" v-if="projects && projects.length == 0 && !justLogged">
|
<div class="text-text/85 mt-8 ml-8 poppis text-[1.2rem]" v-if="projectList && projectList.length == 0 && !justLogged">
|
||||||
Create your first project...
|
Create your first project...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="justLogged" class="text-[2rem]">
|
<div v-if="justLogged" class="text-[2rem]">
|
||||||
The page will refresh soon
|
The page will refresh soon
|
||||||
</div> -->
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -19,20 +19,10 @@ export default defineEventHandler(async event => {
|
|||||||
if (body.name.length < 3) return setResponseStatus(event, 400, 'name too short');
|
if (body.name.length < 3) return setResponseStatus(event, 400, 'name too short');
|
||||||
if (body.name.length > 32) return setResponseStatus(event, 400, 'name too long');
|
if (body.name.length > 32) return setResponseStatus(event, 400, 'name too long');
|
||||||
|
|
||||||
const userData = getRequestUser(event);
|
const data = await getRequestData(event, { allowGuests: false, allowLitlyx: false, });
|
||||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
if (!data) return;
|
||||||
|
|
||||||
const currentActiveProject = await UserSettingsModel.findOne({ user_id: userData.id });
|
const { project_id } = data;
|
||||||
if (!currentActiveProject) return setResponseStatus(event, 400, 'You need to select a project');
|
|
||||||
|
|
||||||
const project_id = currentActiveProject.active_project_id;
|
|
||||||
|
|
||||||
const project = await ProjectModel.findById(project_id);
|
|
||||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
|
||||||
|
|
||||||
if (project.owner.toString() != userData.id) {
|
|
||||||
return setResponseStatus(event, 400, 'You are not the owner');
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = generateApiKey();
|
const key = generateApiKey();
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
|
|
||||||
import { ApiSettingsModel } from "@schema/ApiSettingsSchema";
|
import { ApiSettingsModel } from "@schema/ApiSettingsSchema";
|
||||||
import { UserSettingsModel } from "@schema/UserSettings";
|
|
||||||
import { ProjectModel } from "@schema/ProjectSchema";
|
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
|
|
||||||
const userData = getRequestUser(event);
|
const data = await getRequestData(event, { allowGuests: false, allowLitlyx: false, });
|
||||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
if (!data) return;
|
||||||
|
|
||||||
const currentActiveProject = await UserSettingsModel.findOne({ user_id: userData.id });
|
const { project_id } = data;
|
||||||
if (!currentActiveProject) return setResponseStatus(event, 400, 'You need to select a project');
|
|
||||||
|
|
||||||
const project_id = currentActiveProject.active_project_id;
|
const deletation = await ApiSettingsModel.deleteOne({ project_id, _id: body.api_id });
|
||||||
|
|
||||||
const project = await ProjectModel.findById(project_id);
|
|
||||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
|
||||||
|
|
||||||
if (project.owner.toString() != userData.id) {
|
|
||||||
return setResponseStatus(event, 400, 'You are not the owner');
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletation = await ApiSettingsModel.deleteOne({ _id: body.api_id });
|
|
||||||
return { ok: deletation.acknowledged };
|
return { ok: deletation.acknowledged };
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
|
|
||||||
import { getUserProjectFromId } from "~/server/LIVE_DEMO_DATA";
|
|
||||||
import { ApiSettingsModel, TApiSettings } from "@schema/ApiSettingsSchema";
|
import { ApiSettingsModel, TApiSettings } from "@schema/ApiSettingsSchema";
|
||||||
import { UserSettingsModel } from "@schema/UserSettings";
|
|
||||||
import { ProjectModel } from "@schema/ProjectSchema";
|
|
||||||
|
|
||||||
|
|
||||||
function cryptApiKeyName(apiSettings: TApiSettings): TApiSettings {
|
function cryptApiKeyName(apiSettings: TApiSettings): TApiSettings {
|
||||||
return { ...apiSettings, apiKey: apiSettings.apiKey.substring(0, 6) + '******' }
|
return { ...apiSettings, apiKey: apiSettings.apiKey.substring(0, 6) + '******' }
|
||||||
@@ -11,23 +7,12 @@ function cryptApiKeyName(apiSettings: TApiSettings): TApiSettings {
|
|||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const userData = getRequestUser(event);
|
const data = await getRequestData(event, { allowGuests: false, allowLitlyx: false, requireRange: false });
|
||||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
if (!data) return;
|
||||||
|
|
||||||
const currentActiveProject = await UserSettingsModel.findOne({ user_id: userData.id });
|
const { project_id } = data;
|
||||||
if (!currentActiveProject) return setResponseStatus(event, 400, 'You need to select a project');
|
|
||||||
|
|
||||||
const project_id = currentActiveProject.active_project_id;
|
|
||||||
|
|
||||||
const project = await ProjectModel.findById(project_id);
|
|
||||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
|
||||||
|
|
||||||
if (project.owner.toString() != userData.id) {
|
|
||||||
return setResponseStatus(event, 400, 'You are not the owner');
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKeys = await ApiSettingsModel.find({ project_id }, { project_id: 0 })
|
const apiKeys = await ApiSettingsModel.find({ project_id }, { project_id: 0 })
|
||||||
|
|
||||||
return apiKeys.map(e => cryptApiKeyName(e.toJSON())) as TApiSettings[];
|
return apiKeys.map(e => cryptApiKeyName(e.toJSON())) as TApiSettings[];
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1,20 +1,15 @@
|
|||||||
import { ProjectModel, TProject } from "@schema/ProjectSchema";
|
|
||||||
import { ProjectLimitModel } from "@schema/ProjectsLimits";
|
import { ProjectLimitModel } from "@schema/ProjectsLimits";
|
||||||
import { UserSettingsModel } from "@schema/UserSettings";
|
|
||||||
import { MAX_LOG_LIMIT_PERCENT } from '@data/broker/Limits';
|
import { MAX_LOG_LIMIT_PERCENT } from '@data/broker/Limits';
|
||||||
|
|
||||||
export default defineEventHandler(async event => {
|
export default defineEventHandler(async event => {
|
||||||
|
|
||||||
const userData = getRequestUser(event);
|
|
||||||
if (!userData?.logged) return setResponseStatus(event, 400, 'NotLogged');
|
|
||||||
|
|
||||||
const currentActiveProject = await UserSettingsModel.findOne({ user_id: userData.id });
|
|
||||||
if (!currentActiveProject) return setResponseStatus(event, 400, 'You need to select a project');
|
|
||||||
|
|
||||||
const project_id = currentActiveProject.active_project_id;
|
const data = await getRequestData(event);
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
const project = await ProjectModel.findById(project_id);
|
const { project_id } = data;
|
||||||
if (!project) return setResponseStatus(event, 400, 'Project not found');
|
|
||||||
|
|
||||||
const projectLimits = await ProjectLimitModel.findOne({ project_id });
|
const projectLimits = await ProjectLimitModel.findOne({ project_id });
|
||||||
if (!projectLimits) return;
|
if (!projectLimits) return;
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ export function getRequestAddress(event: H3Event<EventHandlerRequest>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type GetRequestDataOptions = {
|
export type GetRequestDataOptions = {
|
||||||
allowGuests?: boolean,
|
/** @default true */ allowGuests?: boolean,
|
||||||
requireSchema?: boolean,
|
/** @default false */ requireSchema?: boolean,
|
||||||
allowLitlyx?: boolean,
|
/** @default true */ allowLitlyx?: boolean,
|
||||||
requireSlice?: boolean
|
/** @default false */ requireSlice?: boolean,
|
||||||
|
/** @default true */ requireRange?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hasAccessToProject(user_id: string, project: TProject) {
|
async function hasAccessToProject(user_id: string, project: TProject) {
|
||||||
@@ -48,6 +48,7 @@ export async function getRequestData(event: H3Event<EventHandlerRequest>, option
|
|||||||
const allowGuests = options?.allowGuests || true;
|
const allowGuests = options?.allowGuests || true;
|
||||||
const allowLitlyx = options?.allowLitlyx || true;
|
const allowLitlyx = options?.allowLitlyx || true;
|
||||||
const requireSlice = options?.requireSlice || false;
|
const requireSlice = options?.requireSlice || false;
|
||||||
|
const requireRange = options?.requireRange || false;
|
||||||
|
|
||||||
const pid = getHeader(event, 'x-pid');
|
const pid = getHeader(event, 'x-pid');
|
||||||
if (!pid) return setResponseStatus(event, 400, 'x-pid is required');
|
if (!pid) return setResponseStatus(event, 400, 'x-pid is required');
|
||||||
@@ -57,7 +58,9 @@ export async function getRequestData(event: H3Event<EventHandlerRequest>, option
|
|||||||
|
|
||||||
const from = getRequestHeader(event, 'x-from');
|
const from = getRequestHeader(event, 'x-from');
|
||||||
const to = getRequestHeader(event, 'x-to');
|
const to = getRequestHeader(event, 'x-to');
|
||||||
if (!from || !to) return setResponseStatus(event, 400, 'x-from and x-to are required');
|
if (requireRange) {
|
||||||
|
if (!from || !to) return setResponseStatus(event, 400, 'x-from and x-to are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let model: Model<any> = undefined as any;
|
let model: Model<any> = undefined as any;
|
||||||
|
|||||||
Reference in New Issue
Block a user