Fix installer, add favicon, improve editor and layout

This commit is contained in:
Jetomit_Bio 2026-01-17 21:07:28 +01:00
parent ea90efc902
commit 75556045c9
4 changed files with 110 additions and 47 deletions

View File

@ -1,11 +1,18 @@
#!/bin/bash
set -e
echo "=== Surafino Installer ==="
echo "=============================="
echo " Surafino Installer"
echo "=============================="
# -------- INPUT --------
read -p "Domain (example.com): " DOMAIN
read -p "DB Host: " DB_HOST
read -p "DB Host (127.0.0.1): " DB_HOST
DB_HOST=${DB_HOST:-127.0.0.1}
read -p "DB Port (3306): " DB_PORT
DB_PORT=${DB_PORT:-3306}
read -p "DB User: " DB_USER
read -sp "DB Password: " DB_PASSWORD
echo
@ -13,37 +20,44 @@ read -p "DB Name: " DB_NAME
JWT_SECRET=$(openssl rand -hex 32)
echo "Installing system packages..."
echo "== Installing system packages =="
apt update
apt install -y nginx certbot python3-certbot-nginx mariadb-client nodejs npm
apt install -y nginx certbot python3-certbot-nginx mariadb-client curl ca-certificates
echo "Installing node dependencies..."
echo "== Installing Node.js 20 LTS =="
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
echo "== Node version =="
node -v
npm -v
echo "== Installing dependencies =="
npm install
echo "== Building app =="
npm run build
echo "Creating .env..."
echo "== Creating .env =="
cat > .env <<EOF
DB_HOST=$DB_HOST
DB_PORT=$DB_PORT
DB_USER=$DB_USER
DB_PASSWORD=$DB_PASSWORD
DB_NAME=$DB_NAME
JWT_SECRET=$JWT_SECRET
APP_URL=https://$DOMAIN
EOF
echo "Setting up database..."
echo "== Setting up database =="
sed \
-e "s/{{DB_NAME}}/$DB_NAME/g" \
installer/database.sql > /tmp/db.sql
installer/database.sql > /tmp/surafino.sql
mysql \
-h $DB_HOST \
-P $DB_PORT \
-u $DB_USER \
-p$DB_PASSWORD < /tmp/db.sql
mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p"$DB_PASSWORD" < /tmp/surafino.sql
echo "Configuring nginx..."
echo "== Configuring NGINX =="
sed \
-e "s/{{DOMAIN}}/$DOMAIN/g" \
installer/nginx.conf.template > /etc/nginx/sites-available/surafino
@ -52,17 +66,19 @@ ln -sf /etc/nginx/sites-available/surafino /etc/nginx/sites-enabled/surafino
nginx -t
systemctl reload nginx
echo "Installing PM2..."
echo "== Installing PM2 =="
npm install -g pm2
pm2 start npm --name surafino -- start
pm2 save
pm2 startup systemd -u root --hp /root
echo "Requesting SSL certificate..."
certbot --nginx -d $DOMAIN --non-interactive --agree-tos -m admin@$DOMAIN
echo "== Installing SSL (Let's Encrypt) =="
certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos -m admin@"$DOMAIN" || true
echo "=== INSTALLATION COMPLETE ==="
echo "Admin login:"
echo "=============================="
echo " INSTALLATION COMPLETE "
echo "=============================="
echo " Admin login:"
echo " username: admin"
echo " password: admin"
echo "Please change password immediately!"
echo " !!! CHANGE PASSWORD IMMEDIATELY !!!"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -1,5 +1,11 @@
import "./globals.css";
import Navbar from "@/components/Navbar";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Surafino",
description: "Surafino blog & news platform",
};
export default function RootLayout({
children,
@ -8,9 +14,9 @@ export default function RootLayout({
}) {
return (
<html lang="en">
<body>
<body className="min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
<Navbar />
{children}
<main>{children}</main>
</body>
</html>
);

View File

@ -6,30 +6,35 @@ import Image from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import { Video } from "@/components/extensions/Video";
export default function Editor({
content,
onChange,
}: {
type Props = {
content: string;
onChange: (html: string) => void;
}) {
};
export default function Editor({ content, onChange }: Props) {
const editor = useEditor({
extensions: [
StarterKit,
Image,
Link.configure({ openOnClick: false }),
Link.configure({
openOnClick: false,
}),
Video,
],
content,
immediatelyRender: false, // Next.js 16 SSR fix
immediatelyRender: false, // SSR fix for Next 16
onUpdate({ editor }) {
onChange(editor.getHTML());
},
});
if (!editor) return null;
async function upload(file: File) {
/* =========================
UPLOAD HELPER
========================= */
async function upload(file: File): Promise<{
url: string;
type: "image" | "video";
}> {
const data = new FormData();
data.append("file", file);
@ -42,18 +47,30 @@ export default function Editor({
throw new Error("Upload failed");
}
return res.json() as Promise<{
url: string;
type: "image" | "video";
}>;
return res.json();
}
/* =========================
ADD IMAGE
========================= */
async function addImage(file: File) {
if (!editor) return;
const { url } = await upload(file);
editor.chain().focus().setImage({ src: url }).run();
editor
.chain()
.focus()
.setImage({ src: url })
.run();
}
/* =========================
ADD VIDEO
========================= */
async function addVideo(file: File) {
if (!editor) return;
const { url } = await upload(file);
editor
@ -67,14 +84,26 @@ export default function Editor({
type: file.type,
},
},
{ type: "paragraph" },
{
type: "paragraph",
},
])
.run();
}
if (!editor) {
return (
<div className="border rounded-lg p-4 text-gray-500">
Loading editor
</div>
);
}
return (
<div className="border rounded-lg bg-white dark:bg-gray-900">
{/* TOOLBAR */}
{/* =========================
TOOLBAR
========================= */}
<div
className="
flex flex-wrap gap-2 p-2 border-b
@ -84,7 +113,9 @@ export default function Editor({
>
<button
type="button"
onClick={() => editor.chain().focus().toggleBold().run()}
onClick={() =>
editor.chain().focus().toggleBold().run()
}
className="editor-btn"
>
Bold
@ -92,7 +123,9 @@ export default function Editor({
<button
type="button"
onClick={() => editor.chain().focus().toggleItalic().run()}
onClick={() =>
editor.chain().focus().toggleItalic().run()
}
className="editor-btn"
>
Italic
@ -101,7 +134,11 @@ export default function Editor({
<button
type="button"
onClick={() =>
editor.chain().focus().toggleHeading({ level: 2 }).run()
editor
.chain()
.focus()
.toggleHeading({ level: 2 })
.run()
}
className="editor-btn"
>
@ -115,7 +152,8 @@ export default function Editor({
hidden
accept="image/*"
onChange={e =>
e.target.files && addImage(e.target.files[0])
e.target.files &&
addImage(e.target.files[0])
}
/>
</label>
@ -127,13 +165,16 @@ export default function Editor({
hidden
accept="video/mp4,video/webm"
onChange={e =>
e.target.files && addVideo(e.target.files[0])
e.target.files &&
addVideo(e.target.files[0])
}
/>
</label>
</div>
{/* CONTENT */}
{/* =========================
CONTENT
========================= */}
<EditorContent
editor={editor}
className="