-
-
-
Solutions
-
- {footerNavigation.solutions.map((item) => (
- -
-
- {item.name}
-
-
- ))}
-
-
-
-
-
+
diff --git a/src/client/landing-page/contentSections.ts b/src/client/landing-page/contentSections.ts
index 4f0d148..f8f034b 100644
--- a/src/client/landing-page/contentSections.ts
+++ b/src/client/landing-page/contentSections.ts
@@ -25,12 +25,24 @@ export const features = [
"No SaaS is complete without payments. That's why subscriptions and there necessary webhooks are built-in!",
icon: '💸',
},
+ {
+ name: 'Admin Dashboard',
+ description:
+ "Graphs! Tables! Analytics all in one place! Ooooooooooh! Ahhhhhhhhh!",
+ icon: '📈',
+ },
{
name: 'Email Sending',
description:
"Email sending is built-in and pre-configured. Combine it with Wasp's cron jobs feature to easily send emails to your customers.",
icon: '📧',
},
+ {
+ name: 'OpenAI Integration',
+ description:
+ "Technology is changing rapidly. Ship your new AI-powered app before it's already obsolete!",
+ icon: '🤖',
+ },
{
name: 'Deploy Anywhere',
description: 'You own all your code, so deploy it wherever you want!',
@@ -69,35 +81,26 @@ export const tiers = [
export const faqs = [
{
id: 1,
- question: "What's the best thing about Switzerland?",
+ question: "Why is this amazing SaaS Template free and open-source?",
answer:
- "I don't know, but the flag is a big plus. Lorem ipsum dolor sit amet consectetur adipisicing elit. Quas cupiditate laboriosam fugiat.",
+ "Because open-source is cool, and we love you ❤️",
+ },
+ {
+ id: 2,
+ question: "What's Wasp?",
+ answer:
+ "It's the fastest way to develop full-stack React + NodeJS + Prisma apps. It's what gives this template superpowers.",
},
- // More questions...
];
export const footerNavigation = {
- solutions: [
- { name: 'Hosting', href: '#' },
- { name: 'Data Services', href: '#' },
- { name: 'Uptime Monitoring', href: '#' },
- { name: 'Enterprise Services', href: '#' },
- ],
- support: [
- { name: 'Pricing', href: '#' },
- { name: 'Documentation', href: '#' },
- { name: 'Guides', href: '#' },
- { name: 'API Reference', href: '#' },
+ app: [
+ { name: 'Pricing', href: '#pricing' },
+ { name: 'Documentation', href: '#' }, // TODO: fill in
+ { name: 'Blog', href: '#' },
],
company: [
{ name: 'About', href: '#' },
- { name: 'Blog', href: '#' },
- { name: 'Jobs', href: '#' },
- { name: 'Press', href: '#' },
- { name: 'Partners', href: '#' },
- ],
- legal: [
- { name: 'Claim', href: '#' },
{ name: 'Privacy', href: '#' },
- { name: 'Terms', href: '#' },
+ { name: 'Terms of Service', href: '#' },
],
};
diff --git a/src/server/actions.ts b/src/server/actions.ts
index bad70fd..509a002 100644
--- a/src/server/actions.ts
+++ b/src/server/actions.ts
@@ -1,7 +1,7 @@
import Stripe from 'stripe';
import fetch from 'node-fetch';
import HttpError from '@wasp/core/HttpError.js';
-import type { RelatedObject, User } from '@wasp/entities';
+import type { GptResponse, User } from '@wasp/entities';
import type { GenerateGptResponse, StripePayment } from '@wasp/actions/types';
import type { StripePaymentResult, OpenAIResponse } from './types';
import { UpdateCurrentUser, UpdateUserById } from '@wasp/actions/types';
@@ -56,7 +56,7 @@ type GptPayload = {
temperature: number;
};
-export const generateGptResponse: GenerateGptResponse
= async (
+export const generateGptResponse: GenerateGptResponse = async (
{ instructions, command, temperature },
context
) => {
@@ -106,7 +106,7 @@ export const generateGptResponse: GenerateGptResponse
const json = (await response.json()) as OpenAIResponse;
console.log('response json', json);
- return context.entities.RelatedObject.create({
+ return context.entities.GptResponse.create({
data: {
content: json?.choices[0].message.content,
user: { connect: { id: context.user.id } },
diff --git a/src/server/auth/google.ts b/src/server/auth/google.ts
index c81bc45..d4bb99e 100644
--- a/src/server/auth/google.ts
+++ b/src/server/auth/google.ts
@@ -3,7 +3,9 @@
export async function getUserFields(_context: unknown, args: any) {
console.log('args', args.profile)
const email = args.profile.emails[0].value
- return { email };
+ const adminEmails = process.env.ADMIN_EMAILS?.split(',') || []
+ const isAdmin = adminEmails.includes(email)
+ return { email, isAdmin };
}
export function config() {
diff --git a/src/server/auth/setAdminUsers.ts b/src/server/auth/setAdminUsers.ts
new file mode 100644
index 0000000..e4f4527
--- /dev/null
+++ b/src/server/auth/setAdminUsers.ts
@@ -0,0 +1,11 @@
+import { defineAdditionalSignupFields } from '@wasp/auth/index.js'
+
+export default defineAdditionalSignupFields({
+ isAdmin: (data) => {
+ if (!data.email) {
+ return false;
+ }
+ const adminEmails = process.env.ADMIN_EMAILS?.split(',') || [];
+ return adminEmails.includes(data.email as string);
+ },
+});
\ No newline at end of file
diff --git a/src/server/queries.ts b/src/server/queries.ts
index 85f6c43..2d6cc58 100644
--- a/src/server/queries.ts
+++ b/src/server/queries.ts
@@ -1,7 +1,7 @@
import HttpError from '@wasp/core/HttpError.js';
-import type { DailyStats, RelatedObject, User, PageViewSource } from '@wasp/entities';
+import type { DailyStats, GptResponse, User, PageViewSource } from '@wasp/entities';
import type {
- GetRelatedObjects,
+ GetGptResponses,
GetDailyStats,
GetPaginatedUsers,
} from '@wasp/queries/types';
@@ -15,11 +15,11 @@ type DailyStatsValues = {
weeklyStats: DailyStatsWithSources[];
};
-export const getRelatedObjects: GetRelatedObjects = async (args, context) => {
+export const getGptResponses: GetGptResponses = async (args, context) => {
if (!context.user) {
throw new HttpError(401);
}
- return context.entities.RelatedObject.findMany({
+ return context.entities.GptResponse.findMany({
where: {
user: {
id: context.user.id,
diff --git a/src/server/scripts/usersSeed.ts b/src/server/scripts/usersSeed.ts
index 7e0d2e6..a8cac4f 100644
--- a/src/server/scripts/usersSeed.ts
+++ b/src/server/scripts/usersSeed.ts
@@ -4,10 +4,8 @@ import type { User } from '@wasp/entities';
// in a terminal window run `wasp db seed` to seed your dev database with this data
-let prevUserId = 0;
export function createRandomUser(): Partial {
const user: Partial = {
- id: ++prevUserId,
email: faker.internet.email(),
password: faker.internet.password({
length: 12,
diff --git a/src/server/webhooks/stripe.ts b/src/server/webhooks/stripe.ts
index d3593c8..33b1a54 100644
--- a/src/server/webhooks/stripe.ts
+++ b/src/server/webhooks/stripe.ts
@@ -192,26 +192,9 @@ export const stripeWebhook: StripeWebhook = async (request, response, context) =
}
};
-// MIDDELWARE EXAMPLE
-// const defaultGlobalMiddleware: MiddlewareConfig = new Map([
-// ['helmet', helmet()],
-// ['cors', cors({ origin: config.allowedCORSOrigins })],
-// ['logger', logger('dev')],
-// ['express.json', express.json()],
-// ['express.urlencoded', express.urlencoded({ extended: false })],
-// ['cookieParser', cookieParser()],
-// ]);
-
+// This allows us to override Wasp's defaults and parse the raw body of the request from Stripe to verify the signature
export const stripeMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
middlewareConfig.delete('express.json');
middlewareConfig.set('express.raw', express.raw({ type: 'application/json' }));
return middlewareConfig;
-
- // let updatedMiddlewareConfig = new Map([
- // // New entry as an array: [key, value]
- // ['express.raw', express.raw({ type: 'application/json' })],
- // ...Array.from(middlewareConfig.entries()),
- // ]);
-
- // return updatedMiddlewareConfig;
};
diff --git a/src/server/workers/calculateDailyStats.ts b/src/server/workers/calculateDailyStats.ts
index 0102605..39515aa 100644
--- a/src/server/workers/calculateDailyStats.ts
+++ b/src/server/workers/calculateDailyStats.ts
@@ -1,5 +1,4 @@
import type { DailyStatsJob } from '@wasp/jobs/dailyStatsJob';
-import type { DailyStats } from '@wasp/entities';
import Stripe from 'stripe';
// import { getDailyPageViews, getSources } from './plausibleAnalyticsUtils.js';
import { getDailyPageViews, getSources } from './googleAnalyticsUtils.js';
diff --git a/src/server/workers/googleAnalyticsUtils.ts b/src/server/workers/googleAnalyticsUtils.ts
index 63f69b2..8b2c586 100644
--- a/src/server/workers/googleAnalyticsUtils.ts
+++ b/src/server/workers/googleAnalyticsUtils.ts
@@ -98,6 +98,15 @@ async function getPrevDayViewsChangePercent() {
startDate: '2daysAgo',
endDate: 'yesterday',
},
+
+ ],
+ orderBys: [
+ {
+ dimension: {
+ dimensionName: 'date',
+ },
+ desc: true,
+ },
],
dimensions: [
{
@@ -111,6 +120,7 @@ async function getPrevDayViewsChangePercent() {
],
});
+ console.log('response: ', JSON.stringify(response?.rows, null, 2));
let viewsFromYesterday;
let viewsFromDayBeforeYesterday;
@@ -127,6 +137,7 @@ async function getPrevDayViewsChangePercent() {
console.log('Page views are zero, so no percentage change');
return '0';
}
+ console.table({ viewsFromYesterday, viewsFromDayBeforeYesterday });
const change = ((viewsFromYesterday - viewsFromDayBeforeYesterday) / viewsFromDayBeforeYesterday) * 100;
return change.toFixed(2);