fix: build
This commit is contained in:
@@ -14,7 +14,7 @@ COPY backend/go.mod backend/go.sum ./
|
||||
RUN go mod download
|
||||
COPY backend/ ./
|
||||
COPY --from=frontend-builder /app/frontend/public/ ./web/static/
|
||||
RUN go build -ldflags="-w -s" -o aethera ./cmd
|
||||
RUN find ./web/static -type d -empty -delete && go build -ldflags="-w -s" -o aethera ./cmd
|
||||
|
||||
# Stage 3: Minimal Runtime
|
||||
FROM alpine:3.21
|
||||
|
||||
22
Makefile
22
Makefile
@@ -1,14 +1,22 @@
|
||||
.PHONY: all frontend backend clean dev docker docker-run tests
|
||||
.PHONY: all frontend backend clean dev docker docker-run tests check-static
|
||||
|
||||
all: frontend backend
|
||||
|
||||
frontend:
|
||||
rm -rf frontend/public/dist
|
||||
cd frontend && bun run build
|
||||
rm -rf backend/web/static
|
||||
mkdir -p backend/web/static
|
||||
cp frontend/public/index.html backend/web/static/ 2>/dev/null || true
|
||||
cp -r frontend/public/pages backend/web/static/ 2>/dev/null || true
|
||||
cp -R frontend/public/. backend/web/static/
|
||||
find backend/web/static -type d -empty -delete
|
||||
touch backend/web/static/.gitkeep
|
||||
|
||||
backend:
|
||||
check-static:
|
||||
@test -f backend/web/static/index.html || (echo "missing backend/web/static/index.html; run 'make frontend' first" && exit 1)
|
||||
@test -f backend/web/static/dist/main.js || (echo "missing backend/web/static/dist/main.js; run 'make frontend' first" && exit 1)
|
||||
@test -f backend/web/static/dist/styles.css || (echo "missing backend/web/static/dist/styles.css; run 'make frontend' first" && exit 1)
|
||||
|
||||
backend: check-static
|
||||
cd backend && go build -o ./dist/aethera ./cmd
|
||||
|
||||
clean:
|
||||
@@ -17,7 +25,11 @@ clean:
|
||||
rm -rf backend/web/static
|
||||
|
||||
dev:
|
||||
cd backend && go run ./cmd --listen 0.0.0.0 &
|
||||
rm -rf frontend/public/dist
|
||||
cd frontend && bun run build
|
||||
cd backend && AETHERA_STATIC_DIR=../frontend/public go run ./cmd --listen 0.0.0.0 & \
|
||||
backend_pid=$$!; \
|
||||
trap 'kill $$backend_pid' INT TERM EXIT; \
|
||||
cd frontend && bun run dev
|
||||
|
||||
docker:
|
||||
|
||||
@@ -59,6 +59,7 @@ Open your browser and navigate to the URL to begin using Aethera.
|
||||
You can customize the server behavior with these command-line flags:
|
||||
|
||||
- `--data-dir`: Directory for storing generated images (default: `data`)
|
||||
- `--static-dir`: Directory to serve frontend files from instead of embedded assets (useful for development)
|
||||
- `--listen`: Address to listen on (default: `localhost`)
|
||||
- `--port`: Port to listen on (default: `8080`)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ type cliParams struct {
|
||||
ListenAddr string
|
||||
ListenPort int
|
||||
DataDir string
|
||||
StaticDir string
|
||||
SettingsFile string
|
||||
}
|
||||
|
||||
@@ -41,5 +42,16 @@ func (p *cliParams) Validate() error {
|
||||
return fmt.Errorf("failed to create images directory: %w", err)
|
||||
}
|
||||
|
||||
// Validate Static Directory
|
||||
if p.StaticDir != "" {
|
||||
info, err := os.Stat(p.StaticDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to access static directory: %w", err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return fmt.Errorf("static directory is not a directory: %s", p.StaticDir)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@ var (
|
||||
ListenAddr: getEnvOrDefault("LISTEN", "localhost"),
|
||||
ListenPort: getEnvIntOrDefault("PORT", 8080),
|
||||
DataDir: getEnvOrDefault("DATA_DIR", "./data"),
|
||||
StaticDir: getEnvOrDefault("STATIC_DIR", ""),
|
||||
}
|
||||
rootCmd = &cobra.Command{Use: "aethera"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(¶ms.DataDir, "data-dir", params.DataDir, "Directory to store generated images (env: AETHERA_DATA_DIR)")
|
||||
rootCmd.PersistentFlags().StringVar(¶ms.StaticDir, "static-dir", params.StaticDir, "Directory to serve static frontend files from instead of embedded assets (env: AETHERA_STATIC_DIR)")
|
||||
rootCmd.PersistentFlags().StringVar(¶ms.ListenAddr, "listen", params.ListenAddr, "Address to listen on (env: AETHERA_LISTEN)")
|
||||
rootCmd.PersistentFlags().IntVar(¶ms.ListenPort, "port", params.ListenPort, "Port to listen on (env: AETHERA_PORT)")
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func main() {
|
||||
|
||||
// Start Server
|
||||
rootCmd.Run = func(cmd *cobra.Command, args []string) {
|
||||
server.StartServer(fileStore, params.DataDir, params.ListenAddr, params.ListenPort)
|
||||
server.StartServer(fileStore, params.DataDir, params.StaticDir, params.ListenAddr, params.ListenPort)
|
||||
}
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
|
||||
@@ -13,19 +13,24 @@ import (
|
||||
"reichard.io/aethera/web"
|
||||
)
|
||||
|
||||
func StartServer(settingsStore store.Store, dataDir, listenAddress string, listenPort int) {
|
||||
func StartServer(settingsStore store.Store, dataDir, staticDir, listenAddress string, listenPort int) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Create API Instance - use settingsStore as the unified store for both settings and chat
|
||||
logger := logrus.New()
|
||||
api := api.New(settingsStore, dataDir, logger)
|
||||
|
||||
// Serve embedded static assets
|
||||
// Serve Static Assets
|
||||
if staticDir != "" {
|
||||
logrus.Infof("Serving static assets from directory: %s", staticDir)
|
||||
mux.Handle("GET /", http.FileServer(http.Dir(staticDir)))
|
||||
} else {
|
||||
staticFS, err := fs.Sub(web.Assets, "static")
|
||||
if err != nil {
|
||||
logrus.Fatal("Failed to create static filesystem: ", err)
|
||||
}
|
||||
mux.Handle("GET /", http.FileServer(http.FS(staticFS)))
|
||||
}
|
||||
|
||||
// Serve Generated Data
|
||||
genFS := http.FileServer(http.Dir(path.Join(dataDir, "generated")))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "aethera",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "mkdir -p public/dist && bun build src/main.ts --outdir public/dist --target browser --watch & bunx tailwindcss -i styles.css -o public/dist/styles.css --watch",
|
||||
"build": "bun build src/main.ts --outdir public/dist --target browser && bunx tailwindcss -i styles.css -o public/dist/styles.css --minify",
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
getModels,
|
||||
getSettings,
|
||||
} from '../client';
|
||||
import { ImageRecord } from '../types';
|
||||
import { GenerateImageRequest, ImageRecord } from '../types';
|
||||
import { applyFilter } from '../utils';
|
||||
|
||||
// Constants
|
||||
@@ -26,6 +26,12 @@ interface StoredSettings {
|
||||
}
|
||||
|
||||
// Utilities
|
||||
const errorMessage = (err: unknown): string => {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === 'string') return err;
|
||||
return 'An unexpected error occurred';
|
||||
};
|
||||
|
||||
const fileToDataURL = (file: File): Promise<string> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
@@ -252,8 +258,8 @@ Alpine.data('imageGenerator', () => {
|
||||
});
|
||||
},
|
||||
|
||||
async buildRequestData() {
|
||||
const requestData: any = {
|
||||
async buildRequestData(): Promise<GenerateImageRequest> {
|
||||
const requestData: GenerateImageRequest = {
|
||||
prompt: this.prompt,
|
||||
n: parseInt(this.n.toString()),
|
||||
seed: parseInt(this.seed.toString()),
|
||||
@@ -281,8 +287,8 @@ Alpine.data('imageGenerator', () => {
|
||||
const requestData = await this.buildRequestData();
|
||||
const data = await generateImage(requestData);
|
||||
this.generatedImages.unshift(...data);
|
||||
} catch (err: any) {
|
||||
this.error = err;
|
||||
} catch (err) {
|
||||
this.error = errorMessage(err);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
@@ -294,8 +300,8 @@ Alpine.data('imageGenerator', () => {
|
||||
this.generatedImages = this.generatedImages.filter(
|
||||
(img: ImageRecord) => img.name !== filename,
|
||||
);
|
||||
} catch (err: any) {
|
||||
this.error = err;
|
||||
} catch (err) {
|
||||
this.error = errorMessage(err);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user