init project structer
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Next.js
|
||||||
|
apps/web/.next
|
||||||
|
apps/web/out
|
||||||
|
|
||||||
|
# Go
|
||||||
|
apps/server-go/catsave
|
||||||
|
apps/server-go/static
|
||||||
|
|
||||||
|
# Build
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
|
||||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
||||||
|
|
||||||
99
README.md
Normal file
99
README.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# 🐱 CatSave - Go + Next.js Monorepo
|
||||||
|
|
||||||
|
Single binary application with Go backend + Next.js frontend.
|
||||||
|
|
||||||
|
## 📦 Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
catsave/
|
||||||
|
├── apps/
|
||||||
|
│ ├── web/ # Next.js frontend (static export)
|
||||||
|
│ └── server-go/ # Go backend + static file server
|
||||||
|
├── scripts/ # Build scripts
|
||||||
|
└── dist/ # Build output
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Development
|
||||||
|
|
||||||
|
### Quick Start
|
||||||
|
```bash
|
||||||
|
# 1. Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 2. Option A: Dev Next.js only
|
||||||
|
npm run dev:web # http://localhost:3000
|
||||||
|
|
||||||
|
# 3. Option B: Dev with Go
|
||||||
|
npm run build:dev # Build Next.js + sync to Go
|
||||||
|
npm run dev:go # Run Go server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
**Frontend Development (Next.js):**
|
||||||
|
```bash
|
||||||
|
npm run dev:web # Hot reload on :3000
|
||||||
|
```
|
||||||
|
|
||||||
|
**Full Stack Development (Next.js + Go):**
|
||||||
|
```bash
|
||||||
|
# Terminal 1: Build & sync on changes
|
||||||
|
npm run build:dev
|
||||||
|
|
||||||
|
# Terminal 2: Run Go server
|
||||||
|
npm run dev:go # http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**After changing Next.js code:**
|
||||||
|
```bash
|
||||||
|
npm run build:dev # Re-build and sync
|
||||||
|
# Go server will auto-serve new files
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔨 Build
|
||||||
|
|
||||||
|
### Development Build (Quick)
|
||||||
|
```bash
|
||||||
|
npm run build:dev
|
||||||
|
# → Builds Next.js + syncs to Go static folder
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Binary
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
# → Builds Next.js + Go binary
|
||||||
|
# → Output: dist/catsave (single binary)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Commands
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `npm run dev:web` | Dev Next.js only (port 3000) |
|
||||||
|
| `npm run dev:go` | Run Go server (port 8080) |
|
||||||
|
| `npm run build:web` | Build Next.js static export |
|
||||||
|
| `npm run sync:static` | Copy Next.js → Go static |
|
||||||
|
| `npm run build:dev` | Build + sync (development) |
|
||||||
|
| `npm run build` | Full production build |
|
||||||
|
|
||||||
|
## 🎯 Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Just copy the binary
|
||||||
|
./dist/catsave
|
||||||
|
|
||||||
|
# Runs on http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
- **Go**: API + serve static files (embedded)
|
||||||
|
- **Next.js**: Static export (HTML/CSS/JS)
|
||||||
|
- **Result**: One binary file (~10-30MB)
|
||||||
|
|
||||||
|
## 📝 Tech Stack
|
||||||
|
|
||||||
|
- Go 1.21+
|
||||||
|
- Next.js 14
|
||||||
|
- TypeScript
|
||||||
|
- Tailwind CSS
|
||||||
|
|
||||||
2
apps/server-go/.gitkeep
Normal file
2
apps/server-go/.gitkeep
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# This ensures the directory is tracked by git
|
||||||
|
|
||||||
5
apps/server-go/go.mod
Normal file
5
apps/server-go/go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module github.com/catsave/server
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require github.com/gorilla/mux v1.8.1
|
||||||
2
apps/server-go/go.sum
Normal file
2
apps/server-go/go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
80
apps/server-go/main.go
Normal file
80
apps/server-go/main.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed static/*
|
||||||
|
var staticFiles embed.FS
|
||||||
|
|
||||||
|
type HealthResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
// API Routes
|
||||||
|
api := r.PathPrefix("/api").Subrouter()
|
||||||
|
api.HandleFunc("/health", healthHandler).Methods("GET")
|
||||||
|
api.HandleFunc("/cats", catsHandler).Methods("GET")
|
||||||
|
|
||||||
|
// Serve static files (Next.js build)
|
||||||
|
staticFS, err := fs.Sub(staticFiles, "static")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve static files
|
||||||
|
r.PathPrefix("/").Handler(http.FileServer(http.FS(staticFS)))
|
||||||
|
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
if port == "" {
|
||||||
|
port = "8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("🚀 CatSave server starting on http://localhost:%s\n", port)
|
||||||
|
log.Printf("📱 Web UI: http://localhost:%s\n", port)
|
||||||
|
log.Printf("🔌 API: http://localhost:%s/api/health\n", port)
|
||||||
|
|
||||||
|
if err := http.ListenAndServe(":"+port, r); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
response := HealthResponse{
|
||||||
|
Status: "ok",
|
||||||
|
Message: "Go backend is running! 🐹",
|
||||||
|
Timestamp: time.Now().Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func catsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
cats := []map[string]interface{}{
|
||||||
|
{"id": 1, "name": "Whiskers", "breed": "Persian"},
|
||||||
|
{"id": 2, "name": "Shadow", "breed": "Siamese"},
|
||||||
|
{"id": 3, "name": "Luna", "breed": "British Shorthair"},
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"message": "Hello from Go!",
|
||||||
|
"cats": cats,
|
||||||
|
})
|
||||||
|
}
|
||||||
4
apps/web/.eslintrc.json
Normal file
4
apps/web/.eslintrc.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
||||||
|
|
||||||
5
apps/web/next-env.d.ts
vendored
Normal file
5
apps/web/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||||
21
apps/web/next.config.js
Normal file
21
apps/web/next.config.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
|
||||||
|
// Static Export - Build to pure HTML/CSS/JS
|
||||||
|
output: 'export',
|
||||||
|
|
||||||
|
// Disable features not supported in static export
|
||||||
|
images: {
|
||||||
|
unoptimized: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Optional: add trailing slash
|
||||||
|
trailingSlash: true,
|
||||||
|
|
||||||
|
// Optional: if Go serves from /app path
|
||||||
|
// basePath: '/app',
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
||||||
|
|
||||||
27
apps/web/package.json
Normal file
27
apps/web/package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "@catsave/web",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "^14.2.5",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.12",
|
||||||
|
"@types/react": "^18.3.3",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-next": "^14.2.5",
|
||||||
|
"postcss": "^8.4.39",
|
||||||
|
"tailwindcss": "^3.4.6",
|
||||||
|
"typescript": "^5.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
apps/web/postcss.config.mjs
Normal file
10
apps/web/postcss.config.mjs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/** @type {import('postcss-load-config').Config} */
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
||||||
24
apps/web/src/app/globals.css
Normal file
24
apps/web/src/app/globals.css
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
20
apps/web/src/app/layout.tsx
Normal file
20
apps/web/src/app/layout.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Metadata } from 'next'
|
||||||
|
import './globals.css'
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'CatSave',
|
||||||
|
description: 'Go + Next.js Single Binary Application',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
72
apps/web/src/app/page.tsx
Normal file
72
apps/web/src/app/page.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [apiData, setApiData] = useState<any>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Call Go API
|
||||||
|
fetch('/api/health')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setApiData(data))
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50 to-indigo-50 flex items-center justify-center p-6">
|
||||||
|
<div className="max-w-2xl w-full">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<div className="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-3xl shadow-lg mb-6">
|
||||||
|
<span className="text-5xl">🐱</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl font-bold text-gray-900 mb-4">
|
||||||
|
CatSave
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600 mb-8">
|
||||||
|
Go + Next.js Single Binary
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-3xl shadow-2xl p-8 space-y-6">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-6 border-2 border-blue-100">
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 mb-2">Frontend</h3>
|
||||||
|
<p className="text-sm text-gray-600">Next.js 14 + React 18</p>
|
||||||
|
<p className="text-xs text-blue-600 mt-2">✅ Static Export</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gradient-to-br from-green-50 to-emerald-50 rounded-xl p-6 border-2 border-green-100">
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 mb-2">Backend</h3>
|
||||||
|
<p className="text-sm text-gray-600">Go 1.21+</p>
|
||||||
|
<p className="text-xs text-green-600 mt-2">✅ Embedded</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gradient-to-br from-purple-50 to-pink-50 rounded-xl p-6 border-2 border-purple-100">
|
||||||
|
<h3 className="text-lg font-bold text-gray-900 mb-3">API Status</h3>
|
||||||
|
{apiData ? (
|
||||||
|
<div className="space-y-2 text-sm">
|
||||||
|
<p>✅ Status: <span className="font-mono text-green-600">{apiData.status}</span></p>
|
||||||
|
<p>⏰ Time: <span className="font-mono text-gray-600">{apiData.timestamp}</span></p>
|
||||||
|
<p>🐹 Server: <span className="font-mono text-blue-600">{apiData.message}</span></p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm text-gray-500">Loading...</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center pt-4">
|
||||||
|
<a
|
||||||
|
href="/setup"
|
||||||
|
className="inline-block px-8 py-3 bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold rounded-xl shadow-lg transition-all"
|
||||||
|
>
|
||||||
|
Setup Wizard →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
25
apps/web/src/app/setup/page.tsx
Normal file
25
apps/web/src/app/setup/page.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
export default function SetupPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 via-blue-50 to-indigo-50 flex items-center justify-center p-6">
|
||||||
|
<div className="max-w-2xl w-full">
|
||||||
|
<div className="bg-white rounded-3xl shadow-2xl p-8">
|
||||||
|
<h1 className="text-3xl font-bold text-gray-900 mb-4">
|
||||||
|
Setup Wizard
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-600 mb-6">
|
||||||
|
Configuration page coming soon...
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
className="inline-block px-6 py-2 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium rounded-lg transition-all"
|
||||||
|
>
|
||||||
|
← Back to Home
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
16
apps/web/tailwind.config.ts
Normal file
16
apps/web/tailwind.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: [
|
||||||
|
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
||||||
28
apps/web/tsconfig.json
Normal file
28
apps/web/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
|
|
||||||
6148
package-lock.json
generated
Normal file
6148
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "catsave-monorepo",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"apps/web"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev:web": "npm --workspace @catsave/web run dev",
|
||||||
|
"dev:go": "cd apps/server-go && go run main.go",
|
||||||
|
"build:web": "npm --workspace @catsave/web run build",
|
||||||
|
"sync:static": "bash scripts/sync-static.sh",
|
||||||
|
"build:dev": "npm run build:web && npm run sync:static",
|
||||||
|
"build:go": "bash scripts/build-go.sh",
|
||||||
|
"build": "npm run build:web && npm run build:go"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.3.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
66
scripts/build-go.sh
Normal file
66
scripts/build-go.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "🔨 Building CatSave Single Binary..."
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Step 1: Build Next.js static export
|
||||||
|
echo -e "${BLUE}Step 1: Building Next.js...${NC}"
|
||||||
|
cd apps/web
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
if [ ! -d "out" ]; then
|
||||||
|
echo -e "${YELLOW}❌ Next.js build failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# Step 2: Copy static files to Go project
|
||||||
|
echo -e "${BLUE}Step 2: Copying static files to Go...${NC}"
|
||||||
|
rm -rf apps/server-go/static
|
||||||
|
mkdir -p apps/server-go/static
|
||||||
|
cp -r apps/web/out/* apps/server-go/static/
|
||||||
|
|
||||||
|
echo " ✅ Copied $(ls -1 apps/server-go/static | wc -l) items"
|
||||||
|
|
||||||
|
# Step 3: Build Go binary
|
||||||
|
echo -e "${BLUE}Step 3: Building Go binary...${NC}"
|
||||||
|
cd apps/server-go
|
||||||
|
|
||||||
|
# Get dependencies
|
||||||
|
go mod download
|
||||||
|
|
||||||
|
# Build for Linux
|
||||||
|
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ../../dist/catsave-linux main.go
|
||||||
|
echo " ✅ Built: dist/catsave-linux"
|
||||||
|
|
||||||
|
# Build for current OS
|
||||||
|
go build -ldflags="-s -w" -o ../../dist/catsave main.go
|
||||||
|
echo " ✅ Built: dist/catsave"
|
||||||
|
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
# Get file sizes
|
||||||
|
SIZE_LINUX=$(du -h dist/catsave-linux 2>/dev/null | cut -f1)
|
||||||
|
SIZE_LOCAL=$(du -h dist/catsave 2>/dev/null | cut -f1)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✅ Build complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Output:"
|
||||||
|
echo " - dist/catsave (${SIZE_LOCAL:-N/A})"
|
||||||
|
echo " - dist/catsave-linux (${SIZE_LINUX:-N/A})"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}To run:${NC}"
|
||||||
|
echo " ./dist/catsave"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}To deploy to Linux server:${NC}"
|
||||||
|
echo " scp dist/catsave-linux user@server:/opt/catsave"
|
||||||
|
echo " ssh user@server '/opt/catsave'"
|
||||||
|
echo ""
|
||||||
|
|
||||||
27
scripts/sync-static.sh
Normal file
27
scripts/sync-static.sh
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "🔄 Syncing Next.js static files to Go..."
|
||||||
|
|
||||||
|
# Check if Next.js build exists
|
||||||
|
if [ ! -d "apps/web/out" ]; then
|
||||||
|
echo "❌ Next.js build not found. Run 'npm run build:web' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove old static files (except .gitkeep if exists)
|
||||||
|
echo " - Cleaning old files..."
|
||||||
|
rm -rf apps/server-go/static/*
|
||||||
|
|
||||||
|
# Copy new files
|
||||||
|
echo " - Copying files..."
|
||||||
|
cp -r apps/web/out/* apps/server-go/static/
|
||||||
|
|
||||||
|
# Count files
|
||||||
|
FILE_COUNT=$(find apps/server-go/static -type f | wc -l)
|
||||||
|
|
||||||
|
echo "✅ Synced ${FILE_COUNT} files to apps/server-go/static/"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " cd apps/server-go && go run main.go"
|
||||||
|
echo " Open http://localhost:8080"
|
||||||
|
|
||||||
Reference in New Issue
Block a user