kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: adding demo setup (#83)

authored by

Andrej and committed by
GitHub
2ff307a0 e7bbbcb4

+162 -1
+1
apps/api/package.json
··· 8 8 }, 9 9 "dependencies": { 10 10 "@elysiajs/cors": "^1.2.0", 11 + "@elysiajs/cron": "^1.2.0", 11 12 "@elysiajs/jwt": "^1.2.0", 12 13 "@elysiajs/websocket": "^0.2.8", 13 14 "@kaneo/typescript-config": "workspace:*",
+49 -1
apps/api/src/index.ts
··· 1 1 import path from "node:path"; 2 2 import { cors } from "@elysiajs/cors"; 3 + import { cron } from "@elysiajs/cron"; 3 4 import { migrate } from "drizzle-orm/bun-sqlite/migrator"; 4 5 import { Elysia } from "elysia"; 5 6 import db from "./database"; ··· 7 8 import task from "./task"; 8 9 import user from "./user"; 9 10 import { validateSessionToken } from "./user/controllers/validate-session-token"; 11 + import { createDemoUser } from "./utils/create-demo-user"; 12 + import purgeData from "./utils/purge-demo-data"; 10 13 import workspace from "./workspace"; 11 14 import workspaceUser from "./workspace-user"; 12 15 ··· 14 17 .state("userEmail", "") 15 18 .use(cors()) 16 19 .use(user) 20 + .use( 21 + cron({ 22 + name: "purge-demo-data", 23 + pattern: "0 0 * * *", 24 + run: async () => { 25 + const isDemoMode = process.env.DEMO_MODE === "true"; 26 + 27 + if (isDemoMode) { 28 + console.log("Purging demo data"); 29 + await purgeData(); 30 + } 31 + }, 32 + }), 33 + ) 17 34 .guard({ 18 - async beforeHandle({ store, cookie: { session } }) { 35 + async beforeHandle({ store, cookie: { session }, set }) { 36 + const isDemoMode = process.env.DEMO_MODE === "true"; 37 + 38 + if (isDemoMode && !session?.value) { 39 + const { 40 + id, 41 + name, 42 + email, 43 + session: demoSession, 44 + expiresAt, 45 + } = await createDemoUser(); 46 + 47 + set.cookie = { 48 + session: { 49 + value: demoSession, 50 + httpOnly: true, 51 + path: "/", 52 + secure: process.env.NODE_ENV === "production", 53 + sameSite: "lax", 54 + expires: expiresAt, 55 + }, 56 + }; 57 + 58 + return { 59 + user: { 60 + id, 61 + name, 62 + email, 63 + }, 64 + }; 65 + } 66 + 19 67 if (!session?.value) { 20 68 return { user: null }; 21 69 }
+39
apps/api/src/utils/create-demo-user.ts
··· 1 + import { createId } from "@paralleldrive/cuid2"; 2 + 3 + import db from "../database"; 4 + 5 + import { userTable } from "../database/schema"; 6 + import generateSessionToken from "../user/utils/generate-session-token"; 7 + 8 + import createSession from "../user/controllers/create-session"; 9 + import { generateDemoName } from "./generate-demo-name"; 10 + 11 + export async function createDemoUser() { 12 + const demoId = createId(); 13 + const demoName = generateDemoName(); 14 + const demoEmail = `${demoName}@kaneo.app`; 15 + 16 + const hashedPassword = await Bun.password.hash("demo", { 17 + algorithm: "bcrypt", 18 + }); 19 + await db.insert(userTable).values({ 20 + id: demoId, 21 + name: demoName 22 + .split("-") 23 + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) 24 + .join(" "), 25 + email: demoEmail, 26 + password: hashedPassword, 27 + }); 28 + 29 + const token = generateSessionToken(); 30 + const demoSession = await createSession(token, demoId); 31 + 32 + return { 33 + id: demoId, 34 + name: demoName, 35 + email: demoEmail, 36 + session: token, 37 + expiresAt: demoSession.expiresAt, 38 + }; 39 + }
+55
apps/api/src/utils/generate-demo-name.ts
··· 1 + const adjectives = [ 2 + "fractious", 3 + "whimsical", 4 + "zealous", 5 + "dazzling", 6 + "ethereal", 7 + "tenacious", 8 + "luminous", 9 + "mystical", 10 + "radiant", 11 + "vivacious", 12 + "jubilant", 13 + "serene", 14 + "cosmic", 15 + "dynamic", 16 + "enigmatic", 17 + "mystical", 18 + "radiant", 19 + "vivacious", 20 + "jubilant", 21 + "serene", 22 + "cosmic", 23 + "dynamic", 24 + ]; 25 + 26 + const animals = [ 27 + "monkfish", 28 + "phoenix", 29 + "griffin", 30 + "dragon", 31 + "unicorn", 32 + "kraken", 33 + "sphinx", 34 + "chimera", 35 + "pegasus", 36 + "hydra", 37 + "lynx", 38 + "falcon", 39 + "octopus", 40 + "panther", 41 + "dolphin", 42 + "tiger", 43 + "elephant", 44 + "giraffe", 45 + "hippopotamus", 46 + "kangaroo", 47 + "leopard", 48 + "lion", 49 + ]; 50 + 51 + export function generateDemoName(): string { 52 + const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; 53 + const animal = animals[Math.floor(Math.random() * animals.length)]; 54 + return `${adjective}-${animal}`; 55 + }
+17
apps/api/src/utils/purge-demo-data.ts
··· 1 + import { userTable } from "../database/schema"; 2 + import { projectTable } from "../database/schema"; 3 + import { taskTable } from "../database/schema"; 4 + import { workspaceTable } from "../database/schema"; 5 + import { workspaceUserTable } from "../database/schema"; 6 + 7 + import db from "../database"; 8 + 9 + async function purgeData() { 10 + await db.delete(userTable); 11 + await db.delete(workspaceTable); 12 + await db.delete(workspaceUserTable); 13 + await db.delete(projectTable); 14 + await db.delete(taskTable); 15 + } 16 + 17 + export default purgeData;
bun.lockb

This is a binary file and will not be displayed.

+1
compose.yml compose.demo.yml
··· 55 55 JWT_ACCESS: "change_me" 56 56 DB_PATH: "/app/apps/api/data/kaneo.db" 57 57 RABBITMQ_URL: "amqp://guest:guest@rabbitmq:5672" 58 + DEMO_MODE: "true" 58 59 volumes: 59 60 - sqlite_data:/app/apps/api/data:rw 60 61 networks: