A modern, elegant navigation management system built with Vue 3, Material Design 3, and Turso. Simple to configure, beautiful to use, and easy to deploy.
- 🎨 Material Design 3 - Modern, beautiful UI with Vuetify 3
- 🌓 Dark Mode - Automatic theme switching
- 🔐 Secure Authentication - JWT + bcrypt with HttpOnly cookies
- 👥 Guest Access - Public/private content visibility control
- 📱 Responsive Design - Works perfectly on all devices
- 🚀 Edge Deployment - Deploy to Cloudflare Pages or Vercel
- 💾 Turso Database - Fast, distributed SQLite with Drizzle ORM
- 🔍 Type-Safe - Full TypeScript support
- 📦 Import/Export - Easy data migration
- ⚡ Lightning Fast - Built with Vite 6
- Node.js 18+ and pnpm
- A Turso account (free tier available)
cd textus
pnpm install# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Login to Turso
turso auth login
# Create a database
turso db create textus
# Get database URL and auth token
turso db show textus --url
turso db tokens create textusCopy .env.example to .env:
cp .env.example .envEdit .env:
# Turso Database
TURSO_DATABASE_URL=libsql://your-database.turso.io
TURSO_AUTH_TOKEN=your-auth-token
# Authentication
AUTH_ENABLED=true
AUTH_USERNAME=admin
# Generate with: node -e "console.log(require('bcryptjs').hashSync('yourpassword', 10))"
AUTH_PASSWORD=$2a$10$...
AUTH_SECRET=your-jwt-secret-key-min-32-chars
# Guest Access (optional)
AUTH_REQUIRED_FOR_READ=falsenode -e "console.log(require('bcryptjs').hashSync('yourpassword', 10))"Copy the output to AUTH_PASSWORD in .env.
pnpm db:generate
pnpm db:migrateImportant: You need to start both frontend and backend servers!
# Start both frontend and backend (recommended)
pnpm dev:all
# Or start separately
# Terminal 1:
pnpm dev
# Terminal 2:
pnpm dev:apiVisit http://localhost:5173 🎉
💡 Having issues? Check the detailed Local Development Guide for complete setup steps and troubleshooting.
- Build the project:
pnpm build- Login to Cloudflare:
wrangler login- Create a new Pages project:
wrangler pages project create textus-
Set environment variables in Cloudflare Dashboard:
- Go to Workers & Pages → Your project → Settings → Environment variables
- Add all variables from
.env
-
Deploy:
pnpm deploy:cf- Install Vercel CLI:
pnpm add -g vercel- Login:
vercel login- Set environment variables:
vercel env add TURSO_DATABASE_URL
vercel env add TURSO_AUTH_TOKEN
vercel env add AUTH_ENABLED
vercel env add AUTH_USERNAME
vercel env add AUTH_PASSWORD
vercel env add AUTH_SECRET
vercel env add AUTH_REQUIRED_FOR_READ- Deploy:
pnpm deploy:vercel- AUTH_ENABLED: Enable/disable authentication (
true/false) - AUTH_USERNAME: Admin username
- AUTH_PASSWORD: bcrypt hash of admin password
- AUTH_SECRET: JWT signing secret (min 32 characters)
- AUTH_REQUIRED_FOR_READ: Require auth for viewing content (
true/false)
When AUTH_REQUIRED_FOR_READ=false, unauthenticated users can view content marked as "public". Admins can toggle public/private visibility for each group and site.
- Click "Add Group" to create a navigation category
- Toggle "Public" to make it visible to guests
- Use "Edit Order" to rearrange groups by drag-and-drop
- Click menu (⋮) on group card to edit or delete
- Click the "+" button on a group card to add a site
- Enter site name, URL, optional icon, and description
- Toggle "Public" to control guest visibility
- Click pencil icon to edit, trash icon to delete
- Export: Click menu (⋮) → "Export Data" to download JSON
- Import: Click menu (⋮) → "Import Data" to upload JSON
- Import merges data intelligently (groups by name, sites by URL)
textus/
├── src/ # Vue 3 frontend
│ ├── components/ # Vue components
│ ├── views/ # Page views
│ ├── stores/ # Pinia stores
│ ├── utils/ # API client & utilities
│ ├── styles/ # SASS styles
│ ├── plugins/ # Vuetify plugin
│ ├── router/ # Vue Router
│ └── types/ # TypeScript types
├── server/ # Backend service layer
│ ├── db/ # Drizzle ORM schema
│ ├── api/ # API service
│ └── utils/ # Auth & validation
├── functions/ # Cloudflare Pages Functions
│ └── api/ # API routes
├── drizzle/ # Generated migrations
└── public/ # Static assets
├── textus.svg # Logo/Favicon
├── manifest.json # PWA manifest
├── robots.txt # Search engine directives
├── sitemap.xml # Site map
├── _headers # Security headers (Netlify/CF)
└── .htaccess # Apache configuration
pnpm dev # Start dev server
pnpm build # Build for production
pnpm preview # Preview production build
pnpm typecheck # TypeScript type checking
pnpm lint # Lint code
pnpm format # Format code with Prettier
pnpm db:generate # Generate Drizzle migrations
pnpm db:migrate # Run migrations
pnpm db:studio # Open Drizzle Studio (DB GUI)
pnpm deploy:cf # Deploy to Cloudflare Pages
pnpm deploy:vercel # Deploy to VercelTextus implements comprehensive security measures:
- ✅ JWT authentication with Web Crypto API (HMAC-SHA256)
- ✅ bcrypt password hashing (10 rounds)
- ✅ HttpOnly cookies for token storage (XSS protection)
- ✅ SQL injection prevention (Drizzle ORM parameterized queries)
- ✅ CORS with origin whitelist
- ✅ Login rate limiting (5 attempts per 15 minutes)
- ✅ Request body size limits (1MB max)
- ✅ Input validation and sanitization
- ✅ TypeScript strict mode
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - feel free to use this project for personal or commercial purposes.
- Built with Vue 3
- UI powered by Vuetify 3 (Material Design 3)
- Database by Turso
- ORM by Drizzle
- Icons from Material Design Icons
Made with ❤️ and ☕ by the Textus community
Need help? Found a bug? Open an issue