This commit is contained in:
Emily
2025-04-16 17:13:19 +02:00
parent 946f9d4d32
commit f631c29fb2
12 changed files with 59 additions and 60 deletions

View File

@@ -215,7 +215,7 @@ function copyProjectId() {
</div> </div>
</template> </template>
<template #pdelete> <template #pdelete>
<div class="flex lg:justify-end" v-if="!isGuest"> <div class="flex lg:justify-end pb-30" v-if="!isGuest">
<LyxUiButton type="danger" @click="deleteProject()"> <LyxUiButton type="danger" @click="deleteProject()">
Delete project Delete project
</LyxUiButton> </LyxUiButton>

View File

@@ -6,14 +6,6 @@ definePageMeta({ layout: 'dashboard' });
const { project } = useProject(); const { project } = useProject();
const { isPremium } = useLoggedUser();
const selfhosted = useSelfhosted();
const canDownload = computed(() => {
if (selfhosted) return true;
return isPremium.value;
});
const metricsInfo = ref<number>(0); const metricsInfo = ref<number>(0);
const columns = [ const columns = [
@@ -111,17 +103,10 @@ function goToUpgrade() {
}" v-model="selectedTimeFrom" :options="options"></USelectMenu> }" v-model="selectedTimeFrom" :options="options"></USelectMenu>
</div> </div>
<div v-if="canDownload" @click="downloadCSV()" <div @click="downloadCSV()"
class="bg-[#57c78fc0] hover:bg-[#57c78fab] cursor-pointer text-text poppins font-semibold px-8 py-1 rounded-lg"> class="bg-[#57c78fc0] hover:bg-[#57c78fab] cursor-pointer text-text poppins font-semibold px-8 py-1 rounded-lg">
Download CSV Download CSV
</div> </div>
<div v-if="!canDownload" @click="goToUpgrade()"
class="bg-[#57c78f46] hover:bg-[#57c78f42] flex gap-4 items-center cursor-pointer text-text poppins font-semibold px-8 py-2 rounded-lg">
<i class="far fa-lock"></i>
Upgrade plan for CSV
</div>
</div> </div>

View File

@@ -6,14 +6,6 @@ definePageMeta({ layout: 'dashboard' });
const { project } = useProject(); const { project } = useProject();
const { isPremium } = useLoggedUser();
const selfhosted = useSelfhosted();
const canDownload = computed(() => {
if (selfhosted) return true;
return isPremium.value;
});
const metricsInfo = ref<number>(0); const metricsInfo = ref<number>(0);
const columns = [ const columns = [
@@ -116,17 +108,11 @@ function goToUpgrade() {
}" v-model="selectedTimeFrom" :options="options"></USelectMenu> }" v-model="selectedTimeFrom" :options="options"></USelectMenu>
</div> </div>
<div v-if="canDownload" @click="downloadCSV()" <div @click="downloadCSV()"
class="bg-[#57c78fc0] hover:bg-[#57c78fab] cursor-pointer text-text poppins font-semibold px-8 py-1 rounded-lg"> class="bg-[#57c78fc0] hover:bg-[#57c78fab] cursor-pointer text-text poppins font-semibold px-8 py-1 rounded-lg">
Download CSV Download CSV
</div> </div>
<div v-if="!canDownload" @click="goToUpgrade()"
class="bg-[#57c78f46] hover:bg-[#57c78f42] flex gap-4 items-center cursor-pointer text-text poppins font-semibold px-8 py-2 rounded-lg">
<i class="far fa-lock"></i>
Upgrade plan for CSV
</div>
</div> </div>

View File

@@ -61,7 +61,8 @@ async function registerAccount() {
Sign up Sign up
</div> </div>
<div class="text-lyx-lightmode-text dark:text-lyx-text/80 text-[1.2rem] font-light text-center w-[70%] poppins mt-2"> <div
class="text-lyx-lightmode-text dark:text-lyx-text/80 text-[1.2rem] font-light text-center w-[70%] poppins mt-2">
Track web analytics and custom events Track web analytics and custom events
with extreme simplicity in under 30 sec. with extreme simplicity in under 30 sec.
<br> <br>
@@ -88,8 +89,8 @@ async function registerAccount() {
Password must be at least 6 chars long Password must be at least 6 chars long
</div> </div>
<div class="flex justify-center mt-4 z-[100]"> <div class="flex justify-center mt-4 z-[100]">
<LyxUiButton :disabled="!canRegister" @click="registerAccount()" class="text-center" <LyxUiButton :disabled="!canRegister" @click="canRegister ? registerAccount() : ''"
type="primary"> class="text-center" type="primary">
Sign up Sign up
</LyxUiButton> </LyxUiButton>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { createUserJwt } from '~/server/AuthManager';
import { UserModel } from '@schema/UserSchema'; import { UserModel } from '@schema/UserSchema';
import { EmailService } from '@services/EmailService'; import { EmailService } from '@services/EmailService';
import { EmailServiceHelper } from '~/server/services/EmailServiceHelper'; import { EmailServiceHelper } from '~/server/services/EmailServiceHelper';
import { PaymentServiceHelper } from '~/server/services/PaymentServiceHelper';
const { GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_ID } = useRuntimeConfig() const { GOOGLE_AUTH_CLIENT_SECRET, GOOGLE_AUTH_CLIENT_ID } = useRuntimeConfig()
@@ -38,7 +39,6 @@ export default defineEventHandler(async event => {
const user = await UserModel.findOne({ email: payload.email }); const user = await UserModel.findOne({ email: payload.email });
if (user) { if (user) {
user.google_tokens = tokens as any;
await user.save(); await user.save();
return { return {
error: false, error: false,
@@ -46,28 +46,28 @@ export default defineEventHandler(async event => {
} }
} }
const newUser = new UserModel({ const newUser = new UserModel({
email: payload.email, email: payload.email,
given_name: payload.given_name, given_name: payload.given_name,
name: payload.name, name: payload.name,
locale: payload.locale, locale: payload.locale,
picture: payload.picture, picture: payload.picture,
google_tokens: tokens,
created_at: Date.now() created_at: Date.now()
}); });
const savedUser = await newUser.save(); const savedUser = await newUser.save();
const [ok, error] = await PaymentServiceHelper.create_customer(savedUser.id);
if (!ok) throw error;
setImmediate(() => { setImmediate(() => {
const emailData = EmailService.getEmailServerInfo('brevolist_add', { email: payload.email as string }); if (!payload.email) return;
const emailData = EmailService.getEmailServerInfo('brevolist_add', { email: payload.email });
EmailServiceHelper.sendEmail(emailData); EmailServiceHelper.sendEmail(emailData);
}); });
setImmediate(() => { setImmediate(() => {
console.log('SENDING WELCOME EMAIL TO', payload.email);
if (!payload.email) return; if (!payload.email) return;
const emailData = EmailService.getEmailServerInfo('welcome', { target: payload.email }); const emailData = EmailService.getEmailServerInfo('welcome', { target: payload.email });
EmailServiceHelper.sendEmail(emailData); EmailServiceHelper.sendEmail(emailData);

View File

@@ -4,21 +4,12 @@ import { UserModel } from "@schema/UserSchema";
import { VisitModel } from "@schema/metrics/VisitSchema"; import { VisitModel } from "@schema/metrics/VisitSchema";
import { EventModel } from "~/shared/schema/metrics/EventSchema"; import { EventModel } from "~/shared/schema/metrics/EventSchema";
const { SELFHOSTED } = useRuntimeConfig();
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
const data = await getRequestDataOld(event, { requireSchema: false }); const data = await getRequestData(event, [], []);
if (!data) return; if (!data) return;
const { project, project_id, user } = data; const { project_id } = data;
if (SELFHOSTED.toString() !== 'TRUE' && SELFHOSTED.toString() !== 'true') {
const PREMIUM_TYPE = project.premium_type;
if (PREMIUM_TYPE === 0) return setResponseStatus(event, 400, 'Project not premium');
}
const { mode, slice } = getQuery(event); const { mode, slice } = getQuery(event);

View File

@@ -14,6 +14,7 @@ import { BotTrafficOptionModel } from "~/shared/schema/shields/BotTrafficOptionS
import { TeamMemberModel } from "~/shared/schema/TeamMemberSchema"; import { TeamMemberModel } from "~/shared/schema/TeamMemberSchema";
import { PasswordModel } from "~/shared/schema/PasswordSchema"; import { PasswordModel } from "~/shared/schema/PasswordSchema";
import { PremiumModel } from "~/shared/schema/PremiumSchema"; import { PremiumModel } from "~/shared/schema/PremiumSchema";
import { PaymentServiceHelper } from "~/server/services/PaymentServiceHelper";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {
@@ -35,7 +36,7 @@ export default defineEventHandler(async event => {
const limitdeletation = await UserLimitModel.deleteMany({ user_id: userData.id }); const limitdeletation = await UserLimitModel.deleteMany({ user_id: userData.id });
const notifiesDeletation = await LimitNotifyModel.deleteMany({ user_id: userData.id }); const notifiesDeletation = await LimitNotifyModel.deleteMany({ user_id: userData.id });
// await StripeService.deleteCustomer(premium.customer_id); await PaymentServiceHelper.delete_customer(premium.customer_id);
for (const project of projects) { for (const project of projects) {
const project_id = project._id; const project_id = project._id;

View File

@@ -47,4 +47,8 @@ export class PaymentServiceHelper {
return await this.send('/update_customer_info', { user_id, address }); return await this.send('/update_customer_info', { user_id, address });
} }
static async delete_customer(customer_id: string): PaymentServiceResponse<{ ok: true }> {
return await this.send('/delete_customer', { customer_id });
}
} }

View File

@@ -17,11 +17,23 @@ console.log('Stripe started in', STRIPE_TESTMODE ? 'TESTMODE' : 'LIVEMODE');
const app = express(); const app = express();
const TOKEN = process.env.TOKEN;
if (!TOKEN || TOKEN.length == 0) {
console.log('TOKEN not set');
process.exit();
}
app.use((req, res, next) => { app.use((req, res, next) => {
const token = req.header('x-litlyx-token');
if (token != TOKEN) {
res.status(403).json({ error: 'token not valid' });
return;
}
console.log(req.path); console.log(req.path);
next(); next();
}) });
app.use('/webhook', webhookRouter); app.use('/webhook', webhookRouter);
app.use('/payment', paymentRouter); app.use('/payment', paymentRouter);

View File

@@ -147,3 +147,18 @@ paymentRouter.post('/update_customer_info', json(), async (req, res) => {
res.status(500).json({ error: ex.message }); res.status(500).json({ error: ex.message });
} }
}); });
export const ZBodyDeleteCustomer = z.object({
customer_id: z.string(),
});
paymentRouter.post('/delete_customer', json(), async (req, res) => {
try {
const deleteCustomerData = ZBodyDeleteCustomer.parse(req.body);
await StripeService.deleteCustomer(deleteCustomerData.customer_id);
return sendJson(res, 200, { ok: true });
} catch (ex) {
res.status(500).json({ error: ex.message });
}
});

View File

@@ -38,13 +38,15 @@ async function main() {
const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8'); const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8');
const devContent = ecosystemContent const devContent = ecosystemContent
.replace("$REDIS_URL$", `${REDIS_URL_TESTMODE}`) .replace("$REDIS_URL$", `${REDIS_URL_TESTMODE}`)
.replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_TESTMODE}`); .replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_TESTMODE}`)
.replace("$DEV_MODE$", `true`);
archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' }); archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' });
} else { } else {
const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8'); const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8');
const devContent = ecosystemContent const devContent = ecosystemContent
.replace("$REDIS_URL$", `${REDIS_URL_PRODUCTION}`) .replace("$REDIS_URL$", `${REDIS_URL_PRODUCTION}`)
.replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_PRODUCTION}`); .replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_PRODUCTION}`)
.replace("$DEV_MODE$", `false`);
archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' }); archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' });
} }

View File

@@ -38,13 +38,15 @@ async function main() {
const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8'); const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8');
const devContent = ecosystemContent const devContent = ecosystemContent
.replace("$REDIS_URL$", `${REDIS_URL_TESTMODE}`) .replace("$REDIS_URL$", `${REDIS_URL_TESTMODE}`)
.replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_TESTMODE}`); .replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_TESTMODE}`)
.replace("$DEV_MODE$", `true`);
archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' }); archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' });
} else { } else {
const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8'); const ecosystemContent = fs.readFileSync(LOCAL_PATH + '/ecosystem.config.js', 'utf8');
const devContent = ecosystemContent const devContent = ecosystemContent
.replace("$REDIS_URL$", `${REDIS_URL_PRODUCTION}`) .replace("$REDIS_URL$", `${REDIS_URL_PRODUCTION}`)
.replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_PRODUCTION}`); .replace("$MONGO_CONNECTION_STRING$", `${DATABASE_CONNECTION_STRING_PRODUCTION}`)
.replace("$DEV_MODE$", `true`);
archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' }); archive.append(Buffer.from(devContent), { name: '/ecosystem.config.js' });
} }