Skip to content
This repository has been archived by the owner on Jan 16, 2025. It is now read-only.

Commit

Permalink
Introduce PNPM, Workspaces and Remix app (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitrisnl authored Jul 19, 2023
1 parent dbff684 commit 00e3748
Show file tree
Hide file tree
Showing 274 changed files with 16,625 additions and 37,965 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ tmp


# temp
web
docker
docker
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
build
node_modules
tmp
dist
.cache
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"semi": true,
"arrowParens": "always",
"bracketSpacing": false,
"quoteProps": "consistent"
"quoteProps": "consistent",
"plugins": ["prettier-plugin-packagejson", "prettier-plugin-tailwindcss"]
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions apps/api/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"root": true,
"parserOptions": {
"tsconfigRootDir": "."
},
"extends": ["custom-adonis"],
"settings": {
"import/resolver": {
"node": {
"extensions": [".ts", ".tsx"],
"moduleDirectory": ["src", "node_modules"]
}
}
}
}
6 changes: 6 additions & 0 deletions apps/api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
build
dist
coverage
.env
tmp
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {editUser} from '../useCases/editUser';
import {requestPasswordReset} from '../useCases/requestPasswordReset';
import {resetPassword} from '../useCases/resetPassword';
import {verifyEmail} from '../useCases/verifyEmail';
import {verifyPasswordReset} from '../useCases/verifyPasswordReset';
import {whoAmI} from '../useCases/whoAmI';

export class UserController {
Expand Down Expand Up @@ -71,6 +72,25 @@ export class UserController {
return response.ok(null);
}

async verifyPasswordReset({request, response}: HttpContextContract) {
const payload = request.body();
const {validate, execute} = verifyPasswordReset({
passwordResetService: getPasswordResetService(),
});
const props = await validate(payload);
const result = await execute(props);

if (E.isLeft(result)) {
const error = result.left;
switch (error) {
case 'InvalidTokenError':
return response.badRequest({message: 'Token is invalid'});
}
}

return response.ok(null);
}

async changePassword({request, auth, response}: HttpContextContract) {
const payload = request.body();
const user = auth.user!;
Expand All @@ -91,11 +111,11 @@ export class UserController {
}

async verifyEmail({request, response}: HttpContextContract) {
const params = request.params();
const payload = request.body();
const {validate, execute} = verifyEmail({
verifyEmailService: getVerifyEmailService(),
});
const props = await validate(params);
const props = await validate(payload);
const result = await execute(props);

if (E.isLeft(result)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class EmailVerificationMailer extends AppMailer {
.subject('Welcome Onboard!')
.htmlView('emails/welcome', {
user: this.user,
url: `${this.baseUrl}/verification-url?token=${this.token}`,
url: `${this.baseUrl}/verify-email?token=${this.token}`,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class PasswordResetMailer extends AppMailer {
.subject('Password Reset')
.htmlView('emails/password-reset', {
user: this.user,
url: `${this.baseUrl}?token=${this.token}`,
url: `${this.baseUrl}/reset-password/?token=${this.token}`,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ Route.group(() => {
return new UserController().requestPasswordReset(ctx);
});

Route.post('change', async (ctx) => {
Route.post('verify', async (ctx) => {
const {UserController} = await import('./controllers/UserController');
return new UserController().verifyPasswordReset(ctx);
});

Route.patch('change', async (ctx) => {
const {UserController} = await import('./controllers/UserController');
return new UserController().changePassword(ctx);
}).middleware('auth');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ interface Dependencies {
export function requestPasswordReset({passwordResetService}: Dependencies) {
async function execute(props: Props): Promise<Response> {
const {email} = props;

const user = await User.query().where('email', email).first();

if (!user) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {verifyPasswordReset} from './verifyPasswordReset';
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {schema, validator} from '@ioc:Adonis/Core/Validator';

import {uuidTokenSchema} from '../../constants/validations';

const validatorSchema = schema.create({
token: uuidTokenSchema,
});

export async function validate(props: Record<string, unknown>) {
const {token} = await validator.validate({
schema: validatorSchema,
data: props,
messages: {
'token.required': 'Please enter a token',
'token.uuid': 'Please enter a valid token',
},
});
return {token};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as E from 'fp-ts/Either';

import User from '../../models/User';
import {PasswordResetService} from '../../services/passwordResetService';
import {validate} from './validation';

type Response = E.Either<'InvalidTokenError', User>;

interface Props {
token: string;
}

interface Dependencies {
passwordResetService: PasswordResetService;
}

export function verifyPasswordReset({passwordResetService}: Dependencies) {
async function execute(props: Props): Promise<Response> {
const {token} = props;
const user = await passwordResetService.getUserByToken(token);

if (!user) {
return E.left('InvalidTokenError');
}

return E.right(user);
}

return {
execute,
validate,
};
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default class extends BaseSchema {
this.schema.createTable(this.tableName, (table) => {
table.string('id').notNullable().primary();

table.string('name').notNullable();
table.string('name', 255).notNullable();
table.string('slug').notNullable().unique();

table.timestamp('created_at', {useTz: true});
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions server/package.json → apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@
"@faker-js/faker": "^8.0.2",
"@japa/preset-adonis": "^1.2.0",
"@japa/runner": "^2.5.1",
"@types/luxon": "^3.3.0",
"@types/proxy-addr": "^2.0.0",
"@types/source-map-support": "^0.5.6",
"adonis-preset-ts": "^2.1.0",
"eslint-config-custom-adonis": "workspace:*",
"pino-pretty": "^10.0.0",
"youch": "^3.2.3",
"youch-terminal": "^2.2.0"
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 4 additions & 3 deletions server/start/events.ts → apps/api/start/events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// import Mail from '@ioc:Adonis/Addons/Mail';
import Mail from '@ioc:Adonis/Addons/Mail';
import Application from '@ioc:Adonis/Core/Application';
import Event from '@ioc:Adonis/Core/Event';
import Database from '@ioc:Adonis/Lucid/Database';
Expand All @@ -13,8 +13,9 @@ Event.on('db:query', (query) => {
}
});

Event.on('mail:sent', () => {
Event.on('mail:sent', (data) => {
if (Application.inDev) {
// Mail.prettyPrint(data);
Mail.prettyPrint(data);
console.log(data.message.html);
}
});
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
28 changes: 28 additions & 0 deletions apps/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"root": false,
"extends": "adonis-preset-ts/tsconfig.json",
"include": ["**/*"],
"exclude": ["node_modules", "build"],
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"rootDir": "./",
"sourceMap": true,
"paths": {
"@/*": ["./*"]
},
"moduleResolution": "node",
"types": [
"@adonisjs/core",
"@adonisjs/repl",
"@japa/preset-adonis/build/adonis-typings",
"@adonisjs/lucid",
"@adonisjs/lucid-slugify",
"@adonisjs/auth",
"@adonisjs/mail",
"@adonisjs/view",
"@adonisjs/redis",
"@adonisjs/bouncer"
]
}
}
7 changes: 7 additions & 0 deletions apps/dashboard/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"root": true,
"parserOptions": {
"tsconfigRootDir": "."
},
"extends": ["custom-remix"]
}
6 changes: 6 additions & 0 deletions apps/dashboard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/.cache
/build
/public/build
.env
28 changes: 28 additions & 0 deletions apps/dashboard/app/components/error-feedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {Alert, AlertDescription, AlertTitle} from 'ui-core';

export function ErrorFeedback({
errors = {},
}: {
errors?: Record<string, string>;
}) {
const description =
Object.keys(errors).length > 0 ? (
Object.keys(errors).map((key) => {
const message = errors[key];
return (
<li key={key}>
<AlertDescription>{message}</AlertDescription>
</li>
);
})
) : (
<AlertDescription>Let's try again, shall we?</AlertDescription>
);

return (
<Alert variant="destructive">
<AlertTitle>Something went wrong</AlertTitle>
<ul className="list-disc pl-4">{description}</ul>
</Alert>
);
}
68 changes: 68 additions & 0 deletions apps/dashboard/app/components/layouts/base-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {Link} from '@remix-run/react';
import React from 'react';
import {cn} from 'ui-core';

import TeamSwitcher from '@/components/team-switcher';
import {UserNav} from '@/components/user-nav';
import {useUser} from '@/lib/user';

function MainNav({className, ...props}: React.HTMLAttributes<HTMLElement>) {
return (
<nav
className={cn('flex items-center space-x-4 lg:space-x-6', className)}
{...props}
>
<Link
to="/"
className="hover:text-primary text-sm font-medium transition-colors"
>
Overview
</Link>
<Link
to="/teams"
className="text-muted-foreground hover:text-primary text-sm font-medium transition-colors"
>
Teams
</Link>
<Link
to="/settings"
className="text-muted-foreground hover:text-primary text-sm font-medium transition-colors"
>
Settings
</Link>
</nav>
);
}

export function BaseLayout({
children,
title,
titleSlot,
}: {
children: React.ReactNode;
title: string;
titleSlot?: React.ReactNode;
}) {
const user = useUser();

return (
<div className="flex min-h-screen flex-col">
<div className="border-b shadow-2xl">
<div className="flex h-16 items-center border-b px-4">
<h1 className="text-2xl font-bold tracking-tight">White Label</h1>
<MainNav className="mx-6" />
<div className="ml-auto flex items-center space-x-4">
<UserNav user={user} />
</div>
</div>
<div className="50 flex h-16 items-center px-8">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">{title}</h2>
{titleSlot}
</div>
</div>
</div>
<div className="flex-1 space-y-4 bg-gray-50 p-8 pt-6">{children}</div>
</div>
);
}
16 changes: 16 additions & 0 deletions apps/dashboard/app/components/layouts/guest-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {BoxIcon} from 'lucide-react';
import React from 'react';

export function GuestLayout({children}: {children: React.ReactNode}) {
return (
<div className="flex min-h-screen flex-col items-center gap-10 bg-slate-100 pt-16">
<div className="flex flex-col items-center space-y-4 leading-none">
<div className="h-12 w-12 rounded-full bg-blue-100 p-2">
<BoxIcon className="h-full w-full text-blue-700" />
</div>
<h1 className="text-4xl font-bold">White Label</h1>
</div>
<div>{children}</div>
</div>
);
}
Loading

0 comments on commit 00e3748

Please sign in to comment.