create-atsdc-stack 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/CLAUDE.md +236 -0
- package/CONTRIBUTING.md +342 -342
- package/INSTALLATION.md +359 -359
- package/LICENSE +201 -201
- package/README.md +405 -405
- package/app/.env.example +17 -17
- package/app/.github/labeler.yml +61 -0
- package/app/.github/workflows/browser-tests.yml +101 -0
- package/app/.github/workflows/check.yml +24 -0
- package/app/.github/workflows/greetings.yml +16 -0
- package/app/.github/workflows/label.yml +22 -0
- package/app/.github/workflows/stale.yml +27 -0
- package/app/.github/workflows/summary.yml +34 -0
- package/app/.stylelintrc.json +8 -0
- package/app/README.md +251 -251
- package/app/astro.config.mjs +83 -83
- package/app/drizzle.config.ts +16 -16
- package/app/package.json +66 -52
- package/app/playwright.config.ts +27 -0
- package/app/public/manifest.webmanifest +36 -36
- package/app/pwa-assets.config.ts +8 -0
- package/app/src/components/Card.astro +36 -36
- package/app/src/db/initialize.ts +107 -107
- package/app/src/db/schema.ts +72 -72
- package/app/src/db/validations.ts +158 -158
- package/app/src/layouts/Layout.astro +63 -63
- package/app/src/lib/config.ts +36 -36
- package/app/src/lib/content-converter.ts +141 -141
- package/app/src/lib/dom-utils.ts +230 -230
- package/app/src/lib/exa-search.ts +269 -269
- package/app/src/pages/api/chat.ts +91 -91
- package/app/src/pages/api/posts.ts +350 -350
- package/app/src/pages/index.astro +87 -87
- package/app/src/styles/components/button.scss +152 -152
- package/app/src/styles/components/card.scss +180 -180
- package/app/src/styles/components/form.scss +240 -240
- package/app/src/styles/global.scss +141 -141
- package/app/src/styles/pages/index.scss +80 -80
- package/app/src/styles/reset.scss +83 -83
- package/app/src/styles/variables/globals.scss +96 -96
- package/app/src/styles/variables/mixins.scss +238 -238
- package/app/tests/browser.test.nopause.ts +10 -0
- package/app/tests/browser.test.ts +13 -0
- package/bin/cli.js +1151 -1138
- package/package.json +8 -6
- package/app/.astro/settings.json +0 -5
- package/app/.astro/types.d.ts +0 -1
package/app/drizzle.config.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { Config } from 'drizzle-kit';
|
|
2
|
-
|
|
3
|
-
if (!process.env.DATABASE_URL) {
|
|
4
|
-
throw new Error('DATABASE_URL environment variable is required');
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
schema: './src/db/schema.ts',
|
|
9
|
-
out: './drizzle',
|
|
10
|
-
dialect: 'postgresql',
|
|
11
|
-
dbCredentials: {
|
|
12
|
-
url: process.env.DATABASE_URL,
|
|
13
|
-
},
|
|
14
|
-
verbose: true,
|
|
15
|
-
strict: true,
|
|
16
|
-
} satisfies Config;
|
|
1
|
+
import type { Config } from 'drizzle-kit';
|
|
2
|
+
|
|
3
|
+
if (!process.env.DATABASE_URL) {
|
|
4
|
+
throw new Error('DATABASE_URL environment variable is required');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
schema: './src/db/schema.ts',
|
|
9
|
+
out: './drizzle',
|
|
10
|
+
dialect: 'postgresql',
|
|
11
|
+
dbCredentials: {
|
|
12
|
+
url: process.env.DATABASE_URL,
|
|
13
|
+
},
|
|
14
|
+
verbose: true,
|
|
15
|
+
strict: true,
|
|
16
|
+
} satisfies Config;
|
package/app/package.json
CHANGED
|
@@ -1,52 +1,66 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "atsdc-app",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "ATSDC Stack - Astro application",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "astro dev",
|
|
8
|
-
"build": "astro check && astro build",
|
|
9
|
-
"preview": "astro preview",
|
|
10
|
-
"astro": "astro",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "atsdc-app",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ATSDC Stack - Astro application",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "astro dev",
|
|
8
|
+
"build": "astro check && astro build",
|
|
9
|
+
"preview": "astro preview",
|
|
10
|
+
"astro": "astro",
|
|
11
|
+
"ts": "node --experimental-strip-types",
|
|
12
|
+
"test:chrome": "npx playwright test --headed --browser=chromium",
|
|
13
|
+
"test:firefox": "npx playwright test --headed --browser=firefox",
|
|
14
|
+
"test:safari": "npx playwright test --headed --browser=webkit",
|
|
15
|
+
"test": "npm run test:chrome && npm run test:firefox && npm run test:safari",
|
|
16
|
+
"test:nopause:chrome": "npx playwright test tests/browser.test.nopause.ts --browser=chromium",
|
|
17
|
+
"test:nopause:firefox": "npx playwright test tests/browser.test.nopause.ts --browser=firefox",
|
|
18
|
+
"test:nopause:safari": "npx playwright test tests/browser.test.nopause.ts --browser=webkit",
|
|
19
|
+
"test:nopause": "npm run test:nopause:chrome && npm run test:nopause:firefox && npm run test:nopause:safari",
|
|
20
|
+
"test:nopause:chrome:headed": "npx playwright test tests/browser.test.nopause.ts --headed --browser=chromium",
|
|
21
|
+
"test:nopause:firefox:headed": "npx playwright test tests/browser.test.nopause.ts --headed --browser=firefox",
|
|
22
|
+
"test:nopause:safari:headed": "npx playwright test tests/browser.test.nopause.ts --headed --browser=webkit",
|
|
23
|
+
"check": "astro check && stylelint \"src/**/*.scss\" && astro build",
|
|
24
|
+
"stylelint:fix": "stylelint \"src/**/*.scss\" --fix"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@astrojs/check": "^0.9.6",
|
|
28
|
+
"@astrojs/react": "^4.4.2",
|
|
29
|
+
"@astrojs/vercel": "^9.0.2",
|
|
30
|
+
"@clerk/astro": "^1.4.3",
|
|
31
|
+
"@clerk/clerk-react": "^5.17.3",
|
|
32
|
+
"@rocicorp/zero": "^0.2.0",
|
|
33
|
+
"@vercel/postgres": "^0.10.0",
|
|
34
|
+
"ai": "^5.0.0",
|
|
35
|
+
"astro": "^5.16.6",
|
|
36
|
+
"cheerio": "^1.0.0",
|
|
37
|
+
"drizzle-orm": "^0.36.4",
|
|
38
|
+
"exa-js": "^1.1.1",
|
|
39
|
+
"marked": "^14.1.3",
|
|
40
|
+
"nanoid": "^5.0.9",
|
|
41
|
+
"open-props": "^1.7.17",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
44
|
+
"sass": "^1.82.0",
|
|
45
|
+
"turndown": "^7.2.0",
|
|
46
|
+
"typescript": "^5.7.2",
|
|
47
|
+
"zod": "^3.24.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@playwright/test": "^1.58.2",
|
|
51
|
+
"@types/node": "^22.10.5",
|
|
52
|
+
"@types/react": "^18.3.18",
|
|
53
|
+
"@types/react-dom": "^18.3.5",
|
|
54
|
+
"@types/turndown": "^5.0.5",
|
|
55
|
+
"@vite-pwa/assets-generator": "^0.2.6",
|
|
56
|
+
"dotenv": "^16.6.1",
|
|
57
|
+
"drizzle-kit": "^0.29.1",
|
|
58
|
+
"stylelint": "^16.26.1",
|
|
59
|
+
"stylelint-config-standard-scss": "^14.0.0",
|
|
60
|
+
"vercel": "^39.2.0",
|
|
61
|
+
"vite-plugin-pwa": "^0.21.2"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: './tests',
|
|
5
|
+
fullyParallel: false,
|
|
6
|
+
forbidOnly: !!process.env.CI,
|
|
7
|
+
retries: process.env.CI ? 1 : 0,
|
|
8
|
+
reporter: 'html',
|
|
9
|
+
use: {
|
|
10
|
+
baseURL: 'http://localhost:4321',
|
|
11
|
+
trace: 'on-first-retry',
|
|
12
|
+
},
|
|
13
|
+
projects: [
|
|
14
|
+
{
|
|
15
|
+
name: 'chromium',
|
|
16
|
+
use: { ...devices['Desktop Chrome'] },
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'firefox',
|
|
20
|
+
use: { ...devices['Desktop Firefox'] },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'webkit',
|
|
24
|
+
use: { ...devices['Desktop Safari'] },
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
})
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ATSDC Stack App",
|
|
3
|
-
"short_name": "ATSDC",
|
|
4
|
-
"description": "Progressive Web App built with the ATSDC Stack",
|
|
5
|
-
"start_url": "/",
|
|
6
|
-
"display": "standalone",
|
|
7
|
-
"background_color": "#ffffff",
|
|
8
|
-
"theme_color": "#4f46e5",
|
|
9
|
-
"orientation": "portrait-primary",
|
|
10
|
-
"icons": [
|
|
11
|
-
{
|
|
12
|
-
"src": "/pwa-192x192.png",
|
|
13
|
-
"sizes": "192x192",
|
|
14
|
-
"type": "image/png",
|
|
15
|
-
"purpose": "any"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"src": "/pwa-512x512.png",
|
|
19
|
-
"sizes": "512x512",
|
|
20
|
-
"type": "image/png",
|
|
21
|
-
"purpose": "any"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"src": "/pwa-512x512.png",
|
|
25
|
-
"sizes": "512x512",
|
|
26
|
-
"type": "image/png",
|
|
27
|
-
"purpose": "maskable"
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"categories": [
|
|
31
|
-
"productivity",
|
|
32
|
-
"utilities"
|
|
33
|
-
],
|
|
34
|
-
"screenshots": [],
|
|
35
|
-
"shortcuts": []
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "ATSDC Stack App",
|
|
3
|
+
"short_name": "ATSDC",
|
|
4
|
+
"description": "Progressive Web App built with the ATSDC Stack",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#ffffff",
|
|
8
|
+
"theme_color": "#4f46e5",
|
|
9
|
+
"orientation": "portrait-primary",
|
|
10
|
+
"icons": [
|
|
11
|
+
{
|
|
12
|
+
"src": "/pwa-192x192.png",
|
|
13
|
+
"sizes": "192x192",
|
|
14
|
+
"type": "image/png",
|
|
15
|
+
"purpose": "any"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"src": "/pwa-512x512.png",
|
|
19
|
+
"sizes": "512x512",
|
|
20
|
+
"type": "image/png",
|
|
21
|
+
"purpose": "any"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"src": "/pwa-512x512.png",
|
|
25
|
+
"sizes": "512x512",
|
|
26
|
+
"type": "image/png",
|
|
27
|
+
"purpose": "maskable"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"categories": [
|
|
31
|
+
"productivity",
|
|
32
|
+
"utilities"
|
|
33
|
+
],
|
|
34
|
+
"screenshots": [],
|
|
35
|
+
"shortcuts": []
|
|
36
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* Card Component
|
|
4
|
-
* Demonstrates proper SCSS architecture:
|
|
5
|
-
* - No <style> tag in this file
|
|
6
|
-
* - Styles imported from external SCSS file
|
|
7
|
-
* - Reusable component with props
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
interface Props {
|
|
11
|
-
title?: string;
|
|
12
|
-
href?: string;
|
|
13
|
-
class?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const { title, href, class: className = '' } = Astro.props;
|
|
17
|
-
|
|
18
|
-
// Import component-specific styles
|
|
19
|
-
import '@/styles/components/card.scss';
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
{href ? (
|
|
23
|
-
<a href={href} class={`card-component ${className}`}>
|
|
24
|
-
{title && <h3 class="card-component__title">{title}</h3>}
|
|
25
|
-
<div class="card-component__content">
|
|
26
|
-
<slot />
|
|
27
|
-
</div>
|
|
28
|
-
</a>
|
|
29
|
-
) : (
|
|
30
|
-
<div class={`card-component ${className}`}>
|
|
31
|
-
{title && <h3 class="card-component__title">{title}</h3>}
|
|
32
|
-
<div class="card-component__content">
|
|
33
|
-
<slot />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
)}
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Card Component
|
|
4
|
+
* Demonstrates proper SCSS architecture:
|
|
5
|
+
* - No <style> tag in this file
|
|
6
|
+
* - Styles imported from external SCSS file
|
|
7
|
+
* - Reusable component with props
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
title?: string;
|
|
12
|
+
href?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { title, href, class: className = '' } = Astro.props;
|
|
17
|
+
|
|
18
|
+
// Import component-specific styles
|
|
19
|
+
import '@/styles/components/card.scss';
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
{href ? (
|
|
23
|
+
<a href={href} class={`card-component ${className}`}>
|
|
24
|
+
{title && <h3 class="card-component__title">{title}</h3>}
|
|
25
|
+
<div class="card-component__content">
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
28
|
+
</a>
|
|
29
|
+
) : (
|
|
30
|
+
<div class={`card-component ${className}`}>
|
|
31
|
+
{title && <h3 class="card-component__title">{title}</h3>}
|
|
32
|
+
<div class="card-component__content">
|
|
33
|
+
<slot />
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
package/app/src/db/initialize.ts
CHANGED
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
import { sql } from '@vercel/postgres';
|
|
2
|
-
import { drizzle } from 'drizzle-orm/vercel-postgres';
|
|
3
|
-
import * as schema from './schema';
|
|
4
|
-
import { posts, comments } from './schema';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Database client configuration
|
|
8
|
-
* Uses Vercel Postgres for production-ready connection pooling
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Create Drizzle instance with schema
|
|
12
|
-
export const db = drizzle(sql, { schema });
|
|
13
|
-
|
|
14
|
-
// Export schema for convenience
|
|
15
|
-
export { schema };
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Database initialization utilities
|
|
19
|
-
* Handles table creation, migrations, and initial setup
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Check if the database tables exist by attempting a simple query
|
|
24
|
-
*/
|
|
25
|
-
export async function checkTablesExist(): Promise<boolean> {
|
|
26
|
-
try {
|
|
27
|
-
await db.select().from(posts).limit(1);
|
|
28
|
-
return true;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error('Error checking if tables exist:', error);
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Initialize the database by creating tables if they don't exist
|
|
37
|
-
* Note: In production, use Drizzle migrations instead
|
|
38
|
-
*/
|
|
39
|
-
export async function initializeDatabase(): Promise<void> {
|
|
40
|
-
try {
|
|
41
|
-
const tablesExist = await checkTablesExist();
|
|
42
|
-
|
|
43
|
-
if (!tablesExist) {
|
|
44
|
-
console.log('Database tables do not exist. Please run migrations using:');
|
|
45
|
-
console.log(' npm run db:push');
|
|
46
|
-
console.log(' or');
|
|
47
|
-
console.log(' npm run db:migrate');
|
|
48
|
-
throw new Error('Database tables not initialized');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log('Database tables verified successfully');
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error('Database initialization failed:', error);
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Verify database connection
|
|
60
|
-
*/
|
|
61
|
-
export async function verifyConnection(): Promise<boolean> {
|
|
62
|
-
try {
|
|
63
|
-
await db.select().from(posts).limit(1);
|
|
64
|
-
return true;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error('Database connection failed:', error);
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Get database health status
|
|
73
|
-
*/
|
|
74
|
-
export async function getDatabaseHealth(): Promise<{
|
|
75
|
-
connected: boolean;
|
|
76
|
-
tablesExist: boolean;
|
|
77
|
-
timestamp: Date;
|
|
78
|
-
}> {
|
|
79
|
-
const connected = await verifyConnection();
|
|
80
|
-
const tablesExist = connected ? await checkTablesExist() : false;
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
connected,
|
|
84
|
-
tablesExist,
|
|
85
|
-
timestamp: new Date(),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Seed initial data (optional - for development)
|
|
91
|
-
*/
|
|
92
|
-
export async function seedDatabase(): Promise<void> {
|
|
93
|
-
try {
|
|
94
|
-
// Check if data already exists
|
|
95
|
-
const existingPosts = await db.select().from(posts).limit(1);
|
|
96
|
-
|
|
97
|
-
if (existingPosts.length > 0) {
|
|
98
|
-
console.log('Database already contains data, skipping seed');
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log('Database seeding completed');
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error('Database seeding failed:', error);
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
1
|
+
import { sql } from '@vercel/postgres';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/vercel-postgres';
|
|
3
|
+
import * as schema from './schema';
|
|
4
|
+
import { posts, comments } from './schema';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Database client configuration
|
|
8
|
+
* Uses Vercel Postgres for production-ready connection pooling
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Create Drizzle instance with schema
|
|
12
|
+
export const db = drizzle(sql, { schema });
|
|
13
|
+
|
|
14
|
+
// Export schema for convenience
|
|
15
|
+
export { schema };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Database initialization utilities
|
|
19
|
+
* Handles table creation, migrations, and initial setup
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if the database tables exist by attempting a simple query
|
|
24
|
+
*/
|
|
25
|
+
export async function checkTablesExist(): Promise<boolean> {
|
|
26
|
+
try {
|
|
27
|
+
await db.select().from(posts).limit(1);
|
|
28
|
+
return true;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error checking if tables exist:', error);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Initialize the database by creating tables if they don't exist
|
|
37
|
+
* Note: In production, use Drizzle migrations instead
|
|
38
|
+
*/
|
|
39
|
+
export async function initializeDatabase(): Promise<void> {
|
|
40
|
+
try {
|
|
41
|
+
const tablesExist = await checkTablesExist();
|
|
42
|
+
|
|
43
|
+
if (!tablesExist) {
|
|
44
|
+
console.log('Database tables do not exist. Please run migrations using:');
|
|
45
|
+
console.log(' npm run db:push');
|
|
46
|
+
console.log(' or');
|
|
47
|
+
console.log(' npm run db:migrate');
|
|
48
|
+
throw new Error('Database tables not initialized');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log('Database tables verified successfully');
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Database initialization failed:', error);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Verify database connection
|
|
60
|
+
*/
|
|
61
|
+
export async function verifyConnection(): Promise<boolean> {
|
|
62
|
+
try {
|
|
63
|
+
await db.select().from(posts).limit(1);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Database connection failed:', error);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get database health status
|
|
73
|
+
*/
|
|
74
|
+
export async function getDatabaseHealth(): Promise<{
|
|
75
|
+
connected: boolean;
|
|
76
|
+
tablesExist: boolean;
|
|
77
|
+
timestamp: Date;
|
|
78
|
+
}> {
|
|
79
|
+
const connected = await verifyConnection();
|
|
80
|
+
const tablesExist = connected ? await checkTablesExist() : false;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
connected,
|
|
84
|
+
tablesExist,
|
|
85
|
+
timestamp: new Date(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Seed initial data (optional - for development)
|
|
91
|
+
*/
|
|
92
|
+
export async function seedDatabase(): Promise<void> {
|
|
93
|
+
try {
|
|
94
|
+
// Check if data already exists
|
|
95
|
+
const existingPosts = await db.select().from(posts).limit(1);
|
|
96
|
+
|
|
97
|
+
if (existingPosts.length > 0) {
|
|
98
|
+
console.log('Database already contains data, skipping seed');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('Database seeding completed');
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Database seeding failed:', error);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|