mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-04-11 13:29:13 +02:00
Merge pull request #365 from wasp-lang/update-updateUser-info
remove user lastActiveTimestamp property
This commit is contained in:
commit
40f6b72290
@ -104,7 +104,7 @@
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -212,9 +208,9 @@
|
||||
@@ -207,9 +203,9 @@
|
||||
}
|
||||
|
||||
api paymentsWebhook {
|
||||
|
@ -0,0 +1,11 @@
|
||||
--- template/app/migrations/20250220095333_remove_last_active_timestamp/migration.sql
|
||||
+++ opensaas-sh/app/migrations/20250220095333_remove_last_active_timestamp/migration.sql
|
||||
@@ -0,0 +1,8 @@
|
||||
+/*
|
||||
+ Warnings:
|
||||
+
|
||||
+ - You are about to drop the column `lastActiveTimestamp` on the `User` table. All the data in the column will be lost.
|
||||
+
|
||||
+*/
|
||||
+-- AlterTable
|
||||
+ALTER TABLE "User" DROP COLUMN "lastActiveTimestamp";
|
@ -1,9 +1,9 @@
|
||||
--- template/app/schema.prisma
|
||||
+++ opensaas-sh/app/schema.prisma
|
||||
@@ -14,10 +14,12 @@
|
||||
@@ -13,10 +13,12 @@
|
||||
|
||||
email String? @unique
|
||||
username String? @unique
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
- isAdmin Boolean @default(false)
|
||||
+ isAdmin Boolean @default(true)
|
||||
+ // isMockUser is an extra property for the demo app ensuring that all users can access
|
||||
|
@ -8,7 +8,7 @@
|
||||
const [isAdminFilter, setIsAdminFilter] = useState<boolean | undefined>(undefined);
|
||||
const [statusOptions, setStatusOptions] = useState<SubscriptionStatus[]>([]);
|
||||
const { data, isLoading, error } = useQuery(getPaginatedUsers, {
|
||||
@@ -222,7 +223,7 @@
|
||||
@@ -211,7 +212,7 @@
|
||||
<p className='text-sm text-black dark:text-white'>{user.subscriptionStatus}</p>
|
||||
</div>
|
||||
<div className='col-span-2 flex items-center'>
|
||||
|
@ -1,13 +1,13 @@
|
||||
--- template/app/src/server/scripts/dbSeeds.ts
|
||||
+++ opensaas-sh/app/src/server/scripts/dbSeeds.ts
|
||||
@@ -38,9 +38,11 @@
|
||||
@@ -37,9 +37,11 @@
|
||||
sendNewsletter: false,
|
||||
credits,
|
||||
subscriptionStatus,
|
||||
- lemonSqueezyCustomerPortalUrl: null,
|
||||
- paymentProcessorUserId: hasUserPaidOnStripe ? `cus_test_${faker.string.uuid()}` : null,
|
||||
+ stripeId: hasUserPaidOnStripe ? `cus_test_${faker.string.uuid()}` : null,
|
||||
datePaid: hasUserPaidOnStripe ? faker.date.between({ from: createdAt, to: lastActiveTimestamp }) : null,
|
||||
datePaid: hasUserPaidOnStripe ? faker.date.between({ from: createdAt, to: timePaid }) : null,
|
||||
subscriptionPlan: subscriptionStatus ? faker.helpers.arrayElement(getSubscriptionPaymentPlanIds()) : null,
|
||||
+ // For the demo app, we want to default isMockUser to true so that our admin dash only shows mock users
|
||||
+ // and not real users signing up to test the app
|
||||
|
@ -1,18 +1,18 @@
|
||||
--- template/app/src/user/operations.ts
|
||||
+++ opensaas-sh/app/src/user/operations.ts
|
||||
@@ -52,7 +52,10 @@
|
||||
@@ -38,7 +38,10 @@
|
||||
subscriptionStatus?: SubscriptionStatus[];
|
||||
};
|
||||
type GetPaginatedUsersOutput = {
|
||||
- users: Pick<User, 'id' | 'email' | 'username' | 'lastActiveTimestamp' | 'subscriptionStatus' | 'paymentProcessorUserId'>[];
|
||||
- users: Pick<User, 'id' | 'email' | 'username' | 'subscriptionStatus' | 'paymentProcessorUserId'>[];
|
||||
+ users: Pick<
|
||||
+ User,
|
||||
+ 'id' | 'email' | 'username' | 'lastActiveTimestamp' | 'subscriptionStatus' | 'stripeId'
|
||||
+ 'id' | 'email' | 'username' | 'subscriptionStatus' | 'stripeId'
|
||||
+ >[];
|
||||
totalPages: number;
|
||||
};
|
||||
|
||||
@@ -65,8 +68,10 @@
|
||||
@@ -51,8 +54,10 @@
|
||||
}
|
||||
|
||||
const allSubscriptionStatusOptions = args.subscriptionStatus as Array<string | null> | undefined;
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
const queryResults = await context.entities.User.findMany({
|
||||
skip: args.skip,
|
||||
@@ -79,6 +84,7 @@
|
||||
@@ -65,6 +70,7 @@
|
||||
mode: 'insensitive',
|
||||
},
|
||||
isAdmin: args.isAdmin,
|
||||
@ -33,16 +33,16 @@
|
||||
},
|
||||
{
|
||||
OR: [
|
||||
@@ -103,7 +109,7 @@
|
||||
@@ -88,7 +94,7 @@
|
||||
username: true,
|
||||
isAdmin: true,
|
||||
lastActiveTimestamp: true,
|
||||
subscriptionStatus: true,
|
||||
- paymentProcessorUserId: true,
|
||||
+ stripeId: true,
|
||||
},
|
||||
orderBy: {
|
||||
id: 'desc',
|
||||
@@ -119,6 +125,7 @@
|
||||
@@ -104,6 +110,7 @@
|
||||
mode: 'insensitive',
|
||||
},
|
||||
isAdmin: args.isAdmin,
|
||||
|
@ -18,7 +18,6 @@ entity User {=psl
|
||||
email String? @unique
|
||||
username String?
|
||||
createdAt DateTime @default(now())
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
paymentProcessorUserId String? @unique
|
||||
lemonSqueezyCustomerPortalUrl String? // You can delete this if you're not using Lemon Squeezy as your payments processor.
|
||||
@ -116,7 +115,6 @@ entity User {=psl
|
||||
email String? @unique
|
||||
username String?
|
||||
createdAt DateTime @default(now())
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
//...
|
||||
psl=}
|
||||
|
@ -79,7 +79,7 @@ Authorization on the server-side is the core of your access control logic, and d
|
||||
You can authorize access to server-side operations by adding a check for a logged-in user on the `context.user` object which is passed to all operations in Wasp:
|
||||
|
||||
```tsx title="src/server/actions.ts"
|
||||
export const updateCurrentUser: UpdateCurrentUser<...> = async (args, context) => {
|
||||
export const someServerAction: SomeServerAction<...> = async (args, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401); // throw an error if user is not logged in
|
||||
}
|
||||
|
@ -140,11 +140,6 @@ query getPaginatedUsers {
|
||||
entities: [User]
|
||||
}
|
||||
|
||||
action updateCurrentUserLastActiveTimestamp {
|
||||
fn: import { updateCurrentUserLastActiveTimestamp } from "@src/user/operations",
|
||||
entities: [User]
|
||||
}
|
||||
|
||||
action updateIsUserAdminById {
|
||||
fn: import { updateIsUserAdminById } from "@src/user/operations",
|
||||
entities: [User]
|
||||
|
@ -13,7 +13,6 @@ model User {
|
||||
|
||||
email String? @unique
|
||||
username String? @unique
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
|
||||
paymentProcessorUserId String? @unique
|
||||
|
@ -172,13 +172,10 @@ const UsersTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-12 border-t-4 border-stroke py-4.5 px-4 dark:border-strokedark md:px-6 '>
|
||||
<div className='grid grid-cols-9 border-t-4 border-stroke py-4.5 px-4 dark:border-strokedark md:px-6 '>
|
||||
<div className='col-span-3 flex items-center'>
|
||||
<p className='font-medium'>Email / Username</p>
|
||||
</div>
|
||||
<div className='col-span-3 hidden items-center sm:flex'>
|
||||
<p className='font-medium'>Last Active</p>
|
||||
</div>
|
||||
<div className='col-span-2 flex items-center'>
|
||||
<p className='font-medium'>Subscription Status</p>
|
||||
</div>
|
||||
@ -202,7 +199,7 @@ const UsersTable = () => {
|
||||
data.users.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className='grid grid-cols-12 gap-4 border-t border-stroke py-4.5 px-4 dark:border-strokedark md:px-6 '
|
||||
className='grid grid-cols-9 gap-4 border-t border-stroke py-4.5 px-4 dark:border-strokedark md:px-6 '
|
||||
>
|
||||
<div className='col-span-3 flex items-center'>
|
||||
<div className='flex flex-col gap-1 '>
|
||||
@ -210,14 +207,6 @@ const UsersTable = () => {
|
||||
<p className='text-sm text-black dark:text-white'>{user.username}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-span-3 hidden items-center sm:flex'>
|
||||
<p className='text-sm text-black dark:text-white'>
|
||||
{user.lastActiveTimestamp.toLocaleDateString() +
|
||||
' ' +
|
||||
user.lastActiveTimestamp.toLocaleTimeString()}
|
||||
</p>
|
||||
</div>
|
||||
<div className='col-span-2 flex items-center'>
|
||||
<p className='text-sm text-black dark:text-white'>{user.subscriptionStatus}</p>
|
||||
</div>
|
||||
|
@ -8,7 +8,6 @@ import { routes } from 'wasp/client/router';
|
||||
import { Outlet, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from 'wasp/client/auth';
|
||||
import { useIsLandingPage } from './hooks/useIsLandingPage';
|
||||
import { updateCurrentUserLastActiveTimestamp } from 'wasp/client/operations';
|
||||
|
||||
/**
|
||||
* use this component to wrap all child components
|
||||
@ -28,16 +27,6 @@ export default function App() {
|
||||
return location.pathname.startsWith('/admin');
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
const lastSeenAt = new Date(user.lastActiveTimestamp);
|
||||
const today = new Date();
|
||||
if (today.getTime() - lastSeenAt.getTime() > 5 * 60 * 1000) {
|
||||
updateCurrentUserLastActiveTimestamp({ lastActiveTimestamp: today });
|
||||
}
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash) {
|
||||
const id = location.hash.replace('#', '');
|
||||
|
@ -26,21 +26,20 @@ function generateMockUserData(): MockUserData {
|
||||
const subscriptionStatus = faker.helpers.arrayElement<SubscriptionStatus | null>(['active', 'cancel_at_period_end', 'past_due', 'deleted', null]);
|
||||
const now = new Date();
|
||||
const createdAt = faker.date.past({ refDate: now });
|
||||
const lastActiveTimestamp = faker.date.between({ from: createdAt, to: now });
|
||||
const timePaid = faker.date.between({ from: createdAt, to: now });
|
||||
const credits = subscriptionStatus ? 0 : faker.number.int({ min: 0, max: 10 });
|
||||
const hasUserPaidOnStripe = !!subscriptionStatus || credits > 3
|
||||
return {
|
||||
email: faker.internet.email({ firstName, lastName }),
|
||||
username: faker.internet.userName({ firstName, lastName }),
|
||||
createdAt,
|
||||
lastActiveTimestamp,
|
||||
isAdmin: false,
|
||||
sendNewsletter: false,
|
||||
credits,
|
||||
subscriptionStatus,
|
||||
lemonSqueezyCustomerPortalUrl: null,
|
||||
paymentProcessorUserId: hasUserPaidOnStripe ? `cus_test_${faker.string.uuid()}` : null,
|
||||
datePaid: hasUserPaidOnStripe ? faker.date.between({ from: createdAt, to: lastActiveTimestamp }) : null,
|
||||
datePaid: hasUserPaidOnStripe ? faker.date.between({ from: createdAt, to: timePaid }) : null,
|
||||
subscriptionPlan: subscriptionStatus ? faker.helpers.arrayElement(getSubscriptionPaymentPlanIds()) : null,
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {
|
||||
type UpdateCurrentUserLastActiveTimestamp,
|
||||
type UpdateIsUserAdminById,
|
||||
type GetPaginatedUsers,
|
||||
} from 'wasp/server/operations';
|
||||
@ -31,19 +30,6 @@ export const updateIsUserAdminById: UpdateIsUserAdminById<{ id: string; data: Pi
|
||||
return updatedUser;
|
||||
};
|
||||
|
||||
export const updateCurrentUserLastActiveTimestamp: UpdateCurrentUserLastActiveTimestamp<Pick<User, 'lastActiveTimestamp'>, User> = async ({ lastActiveTimestamp }, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401);
|
||||
}
|
||||
|
||||
return context.entities.User.update({
|
||||
where: {
|
||||
id: context.user.id,
|
||||
},
|
||||
data: {lastActiveTimestamp},
|
||||
});
|
||||
};
|
||||
|
||||
type GetPaginatedUsersInput = {
|
||||
skip: number;
|
||||
cursor?: number | undefined;
|
||||
@ -52,7 +38,7 @@ type GetPaginatedUsersInput = {
|
||||
subscriptionStatus?: SubscriptionStatus[];
|
||||
};
|
||||
type GetPaginatedUsersOutput = {
|
||||
users: Pick<User, 'id' | 'email' | 'username' | 'lastActiveTimestamp' | 'subscriptionStatus' | 'paymentProcessorUserId'>[];
|
||||
users: Pick<User, 'id' | 'email' | 'username' | 'subscriptionStatus' | 'paymentProcessorUserId'>[];
|
||||
totalPages: number;
|
||||
};
|
||||
|
||||
@ -101,7 +87,6 @@ export const getPaginatedUsers: GetPaginatedUsers<GetPaginatedUsersInput, GetPag
|
||||
email: true,
|
||||
username: true,
|
||||
isAdmin: true,
|
||||
lastActiveTimestamp: true,
|
||||
subscriptionStatus: true,
|
||||
paymentProcessorUserId: true,
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user