Skip to content

Commit

Permalink
improvements: dev onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
flipvh committed Jan 9, 2025
1 parent 044b9f2 commit 77368a6
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 47 deletions.
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"scripts": {
"quick": "cross-env PGLITE=true tsx scripts/quick.ts && cross-env PGLITE=true pnpm dev",
"start": "tsx dist/index.js",
"dev": "cross-env NODE_ENV=development tsx --watch src/index.ts",
"dev": "tsx scripts/drizzle-studio.ts && cross-env NODE_ENV=development tsx --watch src/index.ts",
"check:types": "tsc",
"build": "cross-env NODE_ENV=production tsup",
"build:dev": "tsup",
Expand Down
26 changes: 12 additions & 14 deletions backend/scripts/drizzle-studio.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { spawn } from 'node:child_process';
import chalk from 'chalk';

// Function to start Drizzle Studio
export const startDrizzleStudio = () => {
const startDrizzleStudioInBackground = () => {
const studioProcess = spawn('npx', ['drizzle-kit', 'studio'], {
stdio: 'inherit',
shell: true,
detached: true, // Detach the process
stdio: 'ignore', // Ignore its output to let the parent process exit cleanly
shell: true, // Use shell for compatibility
});

studioProcess.on('close', (code) => {
if (code === 0) {
console.log('Drizzle Studio exited successfully.');
} else {
console.error(`Drizzle Studio exited with code ${code}.`);
}
});
// Detach the child process from the parent
studioProcess.unref();

studioProcess.on('error', (err) => {
console.error('Failed to start Drizzle Studio:', err);
});
console.log(' ');
console.log(`${chalk.greenBright.bold('✔')} Drizzle Studio started in the background`);
console.log(' ');
};

startDrizzleStudioInBackground();
2 changes: 2 additions & 0 deletions backend/scripts/quick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ await migrate(db, { migrationsFolder: 'drizzle', migrationsSchema: 'drizzle-back
const res = await db.execute(sql`SELECT * FROM users`);

if (res.rows.length > 0) {
console.info(' ');
console.info(`${chalk.greenBright.bold('✔')} Database is already seeded`);
console.info(' ');
process.exit(0);
}

Expand Down
10 changes: 7 additions & 3 deletions backend/scripts/seeds/organizations/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const SYSTEM_ADMIN_MEMBERSHIP_COUNT = 10;

// Seed organizations with data
export const organizationsSeed = async () => {
console.info('Seeding organizations...');
console.info(' ');
console.info('◔ Seeding organizations...');

const organizationsInTable = await db.select().from(organizationsTable).limit(1);

Expand Down Expand Up @@ -53,7 +54,8 @@ export const organizationsSeed = async () => {
});

await db.insert(organizationsTable).values(organizations).onConflictDoNothing();
console.info('Seeding members and memberships, this can take a while...');
console.info(' ');
console.info('◔ Seeding members and memberships, this can take a while...');

const hashedPassword = await hashPasswordWithArgon('12345678');

Expand Down Expand Up @@ -138,5 +140,7 @@ export const organizationsSeed = async () => {
await db.insert(membershipsTable).values(memberships).onConflictDoNothing();
}

console.info(`${chalk.greenBright.bold('✔')} Created ${ORGANIZATIONS_COUNT} organizations with ${MEMBERS_COUNT} members each.`);
console.info(' ');
console.info(`${chalk.greenBright.bold('✔')} Created ${ORGANIZATIONS_COUNT} organizations with ${MEMBERS_COUNT} members each`);
console.info(' ');
};
6 changes: 5 additions & 1 deletion backend/scripts/seeds/user/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,9 @@ export const userSeed = async () => {
})
.onConflictDoNothing();

console.info(`${chalk.greenBright.bold('✔')} Created admin user with verified email ${adminUser.email} and password ${adminUser.password}.`);
console.info(' ');
console.info(
`${chalk.greenBright.bold('✔')} Created admin user with verified email ${chalk.greenBright.bold(adminUser.email)} and password ${chalk.greenBright.bold(adminUser.password)}.`,
);
console.info(' ');
};
9 changes: 3 additions & 6 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { migrate as pgliteMigrate } from 'drizzle-orm/pglite/migrator';
import { db } from '#/db/db';
import ascii from '#/utils/ascii';
import { env } from '../env';
import { startDrizzleStudio } from '../scripts/drizzle-studio';
import docs from './lib/docs';
import app from './routes';

Expand All @@ -33,9 +32,6 @@ const main = async () => {
await pgMigrate(db, migrateConfig);
}

// Start Drizzle Studio in development mode
if (config.mode === 'development') startDrizzleStudio();

// Start server
serve(
{
Expand All @@ -45,10 +41,11 @@ const main = async () => {
},
() => {
ascii();
console.info(' ');
console.info(
`Open ${chalk.greenBright.bold(config.name)} on ${chalk.cyanBright(config.frontendUrl)}. Backend on ${chalk.cyanBright(config.backendUrl)}`,
`${chalk.greenBright.bold(config.name)} (Frontend) runs on ${chalk.cyanBright.bold(config.frontendUrl)}. Backend: ${chalk.cyanBright.bold(config.backendUrl)}. Docs: ${chalk.cyanBright(`${config.backendUrl}/docs`)}`,
);
console.info(`Read API docs on ${chalk.cyanBright(`${config.backendUrl}/docs`)}`);
console.info(' ');
},
);
};
Expand Down
11 changes: 10 additions & 1 deletion backend/src/lib/mailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@ import { config } from 'config';
import { env } from '../../env';

const sendgrid = new MailService();
// Check if the API key is set
const hasApiKey = !!env.SENDGRID_API_KEY;

sendgrid.setApiKey(env.SENDGRID_API_KEY ?? '');
if (hasApiKey) {
sendgrid.setApiKey(env.SENDGRID_API_KEY ?? '');
}

// Send email, currently hardcoded to use SendGrid but can be changed to any other service
export const emailSender = {
send: async (to: string, subject: string, html: string, replyTo?: string) => {
if (!hasApiKey) {
console.info(`Email to ${to} is not sent because API key is missing.`);
return;
}

await sendgrid.send({
to: env.SEND_ALL_TO_EMAIL || to,
replyTo: replyTo ? replyTo : config.supportEmail,
Expand Down
5 changes: 4 additions & 1 deletion cli/create-cella/src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ export async function create({

if (needsCd) {
// Calculate the relative path between the original working directory and the target folder

console.info('now go to your project using:');
console.info(colors.cyan(` cd ${relativePath}`)); // Adding './' to make it clear it's a relative path
console.info();
Expand All @@ -184,6 +183,10 @@ export async function create({
console.info(colors.cyan(` ${packageManager} seed`));
console.info();

console.info(`You can directly sign in using:`);
console.info(`email: ${colors.greenBright('admin-test@cellajs.com')}`);
console.info(`password: ${colors.greenBright('12345678')}`);
console.info();
console.info(`For more info, check out: ${relativePath}/README.md`);
console.info(`Enjoy building ${projectName} using cella! 🎉`);
console.info();
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
},
"scripts": {
"quick": "cross-env VITE_QUICK=true pnpm dev",
"dev": "cross-env NODE_ENV=development vite --mode development && node --trace-warnings",
"start": "cross-env NODE_ENV=production pnpm preview",
"check:types": "tsc",
"dev": "cross-env NODE_ENV=development vite --mode development && node --trace-warnings",
"build": "tsc && vite build",
"build:dev": "cross-env NODE_ENV=development vite build --mode development",
"preview": "vite preview --port 3000",
Expand Down Expand Up @@ -141,6 +141,7 @@
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"kill-port": "^2.0.1",
"postcss": "^8.4.49",
"postcss-import": "^16.1.0",
"postcss-preset-env": "^10.1.3",
Expand Down
33 changes: 28 additions & 5 deletions frontend/src/alert-config.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { config } from 'config';
import { t } from 'i18next';
import { Info } from 'lucide-react';
import type { MainAlert } from '~/modules/common/main-alert';

// Here you can set app-specific global alerts
export const alertsConfig: MainAlert[] = [
{
const alerts = [];

// Explain how to sign in using test account
if (config.mode === 'development') {
alerts.push({
id: 'test-credentials',
Icon: Info,
className: 'rounded-none border-0 border-t z-10 fixed bottom-0 left-0 right-0',
children: (
<>
<strong className="mr-2">Testing credentials</strong>
<p>
Hi there! New developer? Welcome to Cella! Sign in using <strong>admin-test@cellajs.com</strong> and password <strong>12345678</strong>.
</p>
</>
),
});
}

// In production mode, show a notice that the app is a pre-release version
if (config.mode === 'production') {
alerts.push({
id: 'prerelease',
Icon: Info,
className: 'rounded-none border-0 border-b',
Expand All @@ -14,5 +34,8 @@ export const alertsConfig: MainAlert[] = [
{t('common:experiment_notice.text')}
</>
),
},
];
});
}

// Here you can set app-specific global alerts
export const alertsConfig: MainAlert[] = alerts;
2 changes: 1 addition & 1 deletion frontend/src/modules/auth/sign-up-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Input } from '~/modules/ui/input';
import type { TokenData } from '.';

const PasswordStrength = lazy(() => import('~/modules/auth/password-strength'));
const LegalText = lazy(() => import('~/modules/marketing/legals-text'));
const LegalText = lazy(() => import('~/modules/marketing/legal-texts'));

const formSchema = authBodySchema;

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/modules/common/public-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { Outlet } from '@tanstack/react-router';
import { Dialoger } from '~/modules/common/dialoger';
import { DropDowner } from '~/modules/common/dropdowner';
import { Sheeter } from '~/modules/common/sheeter';
import AlertRenderer from './main-alert/alert-render';

// Also in public routes, some components need to be initialized.
function PublicLayout() {
return (
<>
<AlertRenderer />
<Dialoger />
<Sheeter />
<DropDowner />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Link } from '@tanstack/react-router';
import { config } from 'config';
import type { LegalTypes } from './legals';
import type { LegalTypes } from './legal';

const LegalText = ({ textFor }: { textFor: LegalTypes }) => {
const LegalTexts = ({ textFor }: { textFor: LegalTypes }) => {
const appName = config.name;
const companyFull = config.company.name;
const companyShort = config.company.shortName;
Expand Down Expand Up @@ -566,4 +566,4 @@ const LegalText = ({ textFor }: { textFor: LegalTypes }) => {
);
};

export default LegalText;
export default LegalTexts;
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { SimpleHeader } from '~/modules/common/simple-header';
import StickyBox from '~/modules/common/sticky-box';
import PublicPage from '~/modules/marketing/page';

const LegalText = lazy(() => import('~/modules/marketing/legals-text'));
const LegalTexts = lazy(() => import('~/modules/marketing/legal-texts'));

export type LegalTypes = 'privacy' | 'terms';

const Legal = ({ type }: { type: LegalTypes }) => {
return (
<section className="bg-background">
<div className="mx-auto max-w-[48rem] font-light px-4 md:px-8 min-h-screen">
<LegalText textFor={type} />
<LegalTexts textFor={type} />
</div>
</section>
);
Expand All @@ -26,7 +26,7 @@ const tabs = [
{ id: 'privacy', label: 'common:privacy_policy' },
] as const;

export const LegalsMenu = () => {
export const LegalMenu = () => {
const { t } = useTranslation();
return (
<PublicPage title={t('common:legal')}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/routes/marketing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createRoute } from '@tanstack/react-router';
import About from '~/modules/marketing/about';
import Accessibility from '~/modules/marketing/accessibility';
import Contact from '~/modules/marketing/contact';
import { LegalsMenu } from '~/modules/marketing/legals';
import { LegalMenu } from '~/modules/marketing/legal';
import { PublicRoute, rootRoute } from './general';

export const AboutRoute = createRoute({
Expand All @@ -23,7 +23,7 @@ export const LegalRoute = createRoute({
path: '/legal',
staticData: { pageTitle: 'Legal', isAuth: false },
getParentRoute: () => rootRoute,
component: () => <LegalsMenu />,
component: () => <LegalMenu />,
});

export const AccessibilityRoute = createRoute({
Expand Down
1 change: 1 addition & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default defineConfig(() => {
server: {
host: '0.0.0.0',
port: Number(frontendUrl.port),
strictPort: true,
},
build: {
rollupOptions: {
Expand Down
11 changes: 8 additions & 3 deletions info/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ This document describes the high-level architecture of Cella.
5. Open standards. Our long term vision is that each Cella - as in each cell - can speak fluently with other cells.

### Backend
- [Hono](https://hono.dev) + [NodeJS](https://nodejs.org)
- [Postgres](https://www.postgresql.org) / [PGLite](https://pglite.dev/) + [Drizzle ORM](https://orm.drizzle.team/)
- [NodeJS](https://nodejs.org)
- [Hono](https://hono.dev)
- [Postgres](https://www.postgresql.org)
- [Drizzle ORM](https://orm.drizzle.team/)
- [Zod](https://github.com/colinhacks/zod)
- [OpenAPI](https://www.openapis.org)
- [Lucia Auth](https://lucia-auth.com/)
Expand All @@ -29,10 +31,13 @@ This document describes the high-level architecture of Cella.
- [Lucide icons](https://lucide.dev)

### Build tools
- [Vite](https://vitejs.dev) + [Vite-PWA](https://github.com/antfu/vite-plugin-pwa)
- [pnpm](https://pnpm.io)
- [Vite](https://vitejs.dev)
- [Vite-PWA](https://github.com/antfu/vite-plugin-pwa)
- [Biome](https://biomejs.dev)
- [Lefthook](https://github.com/evilmartians/lefthook)
- [PGLite](https://pglite.dev/)


## File structure
```
Expand Down
4 changes: 2 additions & 2 deletions info/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pnpm seed
## Customize
1. Customize your config in `/config/default.ts`
2. Update package.json with your own data
3. Look at you .env file to understand what is required and update accordingly
4. There are many config files, which end with '-config.ts'. Here you can set for example your entity structure or your navigation structure.
3. Look at your .env file to understand what is required, for example to send emails you will need an API key.
4. There are many config files with filenames like `-config.ts`. For example for entities or navigation structure.


## Cella CLI
Expand Down
Loading

0 comments on commit 77368a6

Please sign in to comment.