first commit
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
This commit is contained in:
379
info.txt
Normal file
379
info.txt
Normal file
@@ -0,0 +1,379 @@
|
||||
# MEMOS - PROJECT INFO (Bahasa Indonesia)
|
||||
|
||||
## 📋 STRUKTUR PROYEK
|
||||
|
||||
### Arsitektur Utama
|
||||
Proyek Memos adalah aplikasi catatan pribadi berbasis web yang dibangun dengan:
|
||||
- **Backend**: Go (Golang) versi 1.25
|
||||
- **Frontend**: React 18.3 + TypeScript
|
||||
- **Protokol API**: gRPC + Connect RPC
|
||||
- **Database**: SQLite (default), MySQL, PostgreSQL
|
||||
|
||||
### Struktur Direktori Utama
|
||||
|
||||
```
|
||||
memos/
|
||||
├── cmd/memos/ # Entry point aplikasi
|
||||
│ └── main.go # File utama untuk menjalankan server
|
||||
├── server/ # Server HTTP dan routing
|
||||
│ ├── server.go # Konfigurasi Echo server
|
||||
│ ├── router/ # Routing API dan frontend
|
||||
│ │ ├── api/v1/ # Implementasi service gRPC
|
||||
│ │ ├── frontend/ # Serving file statis React
|
||||
│ │ └── fileserver/ # Serving file media
|
||||
│ └── auth/ # Autentikasi (JWT, PAT)
|
||||
├── store/ # Layer data dan database
|
||||
│ ├── db/ # Driver database (sqlite/mysql/postgres)
|
||||
│ ├── migration/ # File migrasi database
|
||||
│ └── *.go # Model dan operasi data
|
||||
├── proto/ # Definisi Protocol Buffer
|
||||
│ ├── api/v1/ # Service dan message definitions
|
||||
│ └── gen/ # Kode yang di-generate
|
||||
├── web/ # Aplikasi frontend React
|
||||
│ ├── src/ # Source code TypeScript
|
||||
│ ├── package.json # Dependencies frontend
|
||||
│ └── vite.config.mts # Konfigurasi Vite
|
||||
└── plugin/ # Plugin tambahan
|
||||
├── markdown/ # Parsing markdown
|
||||
├── email/ # Pengiriman email
|
||||
├── scheduler/ # Cron jobs
|
||||
└── webhook/ # Webhook integration
|
||||
```
|
||||
|
||||
## 🔧 CARA PEMBUATAN API
|
||||
|
||||
### 1. Definisi Protokol Buffer (.proto)
|
||||
|
||||
API didefinisikan dalam file `.proto` di direktori `proto/api/v1/`:
|
||||
|
||||
Contoh: `memo_service.proto`
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
|
||||
package memos.api.v1;
|
||||
|
||||
service MemoService {
|
||||
rpc CreateMemo(CreateMemoRequest) returns (Memo) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/memos"
|
||||
body: "memo"
|
||||
};
|
||||
}
|
||||
|
||||
rpc ListMemos(ListMemosRequest) returns (ListMemosResponse) {
|
||||
option (google.api.http) = {get: "/api/v1/memos"};
|
||||
}
|
||||
}
|
||||
|
||||
message Memo {
|
||||
string name = 1;
|
||||
string content = 7 [(google.api.field_behavior) = REQUIRED];
|
||||
Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED];
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Generate Kode dari Proto
|
||||
|
||||
Jalankan perintah:
|
||||
```bash
|
||||
cd proto && buf generate
|
||||
```
|
||||
|
||||
Ini akan menghasilkan:
|
||||
- Kode Go di `proto/gen/api/v1/` (untuk backend)
|
||||
- Kode TypeScript di `web/src/types/proto/api/v1/` (untuk frontend)
|
||||
|
||||
### 3. Implementasi Service Backend
|
||||
|
||||
File: `server/router/api/v1/memo_service.go`
|
||||
|
||||
```go
|
||||
func (s *APIV1Service) CreateMemo(ctx context.Context, request *v1pb.CreateMemoRequest) (*v1pb.Memo, error) {
|
||||
// 1. Validasi user
|
||||
user, err := s.fetchCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Unauthenticated, "user not authenticated")
|
||||
}
|
||||
|
||||
// 2. Mapping ke model store
|
||||
create := &store.Memo{
|
||||
UID: shortuuid.New(),
|
||||
CreatorID: user.ID,
|
||||
Content: request.Memo.Content,
|
||||
Visibility: convertVisibilityToStore(request.Memo.Visibility),
|
||||
}
|
||||
|
||||
// 3. Simpan ke database
|
||||
memo, err := s.Store.CreateMemo(ctx, create)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. Convert ke response proto
|
||||
return s.convertMemoFromStore(ctx, memo, nil, nil)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Registrasi Service
|
||||
|
||||
File: `server/router/api/v1/v1.go`
|
||||
|
||||
```go
|
||||
// Register gRPC gateway
|
||||
if err := apiV1Service.RegisterGateway(ctx, echoServer); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to register gRPC gateway")
|
||||
}
|
||||
|
||||
// Register Connect handlers
|
||||
connectHandlers := v1pbconnect.NewMemoServiceHandler(apiV1Service)
|
||||
echoServer.POST("/memos.api.v1.MemoService/CreateMemo",
|
||||
connect.NewHandler(connectHandlers.CreateMemo))
|
||||
```
|
||||
|
||||
### 5. Middleware dan Interceptor
|
||||
|
||||
Autentikasi dilakukan melalui interceptor:
|
||||
- **Connect Interceptor**: `connect_interceptors.go`
|
||||
- **gRPC Gateway Interceptor**: Middleware Echo
|
||||
|
||||
## 🖥️ FRONTEND MENGGUNAKAN TEKNOLOGI
|
||||
|
||||
### Framework dan Library Utama
|
||||
|
||||
1. **React 18.3** - Library UI utama
|
||||
2. **TypeScript** - Typing untuk JavaScript
|
||||
3. **Vite 7** - Build tool dan development server
|
||||
4. **Tailwind CSS 4** - Styling utility-first
|
||||
5. **React Query (TanStack Query) v5** - State management server
|
||||
6. **Connect RPC** - Komunikasi dengan backend
|
||||
|
||||
### State Management
|
||||
|
||||
#### 1. Server State (React Query)
|
||||
```typescript
|
||||
// hooks/useMemoQueries.ts
|
||||
export const useMemos = (filters: MemoFilters) => {
|
||||
return useQuery({
|
||||
queryKey: memoKeys.list(filters),
|
||||
queryFn: () => memoServiceClient.listMemos({ filter: filters.query }),
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateMemo = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (memo: CreateMemoRequest) =>
|
||||
memoServiceClient.createMemo(memo),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: memoKeys.all });
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
#### 2. Client State (React Context)
|
||||
```typescript
|
||||
// contexts/AuthContext.tsx
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [currentUser, setCurrentUser] = useState<User | undefined>();
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
await authServiceClient.signOut({});
|
||||
setCurrentUser(undefined);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ currentUser, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Komunikasi API
|
||||
|
||||
File: `web/src/connect.ts`
|
||||
|
||||
```typescript
|
||||
// Transport dengan autentikasi
|
||||
const transport = createConnectTransport({
|
||||
baseUrl: window.location.origin,
|
||||
useBinaryFormat: true,
|
||||
interceptors: [authInterceptor], // Menangani JWT token
|
||||
});
|
||||
|
||||
// Client service
|
||||
export const memoServiceClient = createClient(MemoService, transport);
|
||||
export const userServiceClient = createClient(UserService, transport);
|
||||
```
|
||||
|
||||
### Routing
|
||||
|
||||
Menggunakan React Router DOM v7:
|
||||
```typescript
|
||||
// router/index.tsx
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
children: [
|
||||
{ index: true, element: <Home /> },
|
||||
{ path: "/m/:memoId", element: <MemoDetail /> },
|
||||
{ path: "/auth", element: <AuthLayout /> },
|
||||
],
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
### Styling
|
||||
|
||||
Menggunakan Tailwind CSS dengan komponen UI:
|
||||
```tsx
|
||||
// components/ui/button.tsx
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive: "bg-destructive text-destructive-foreground",
|
||||
outline: "border border-input bg-background hover:bg-accent",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## ⚙️ ARSITEKTUR API DUAL PROTOCOL
|
||||
|
||||
### 1. Connect RPC (Untuk Browser)
|
||||
- Path: `/memos.api.v1.MemoService/*`
|
||||
- Format: Binary protocol buffers
|
||||
- Digunakan oleh frontend React
|
||||
|
||||
### 2. gRPC-Gateway (REST API)
|
||||
- Path: `/api/v1/memos`
|
||||
- Format: JSON over HTTP
|
||||
- Berguna untuk integrasi eksternal
|
||||
|
||||
Keduanya menggunakan implementasi service yang sama, sehingga konsisten.
|
||||
|
||||
## 🔐 AUTENTIKASI
|
||||
|
||||
### Metode Token
|
||||
1. **JWT Access Token V2** - Stateless, 15 menit expiry
|
||||
2. **Personal Access Token (PAT)** - Stateful, long-lived
|
||||
|
||||
### Flow Autentikasi
|
||||
```typescript
|
||||
// Frontend interceptor
|
||||
const authInterceptor: Interceptor = (next) => async (req) => {
|
||||
const token = getAccessToken();
|
||||
if (token) {
|
||||
req.header.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return await next(req);
|
||||
} catch (error) {
|
||||
if (error.code === Code.Unauthenticated) {
|
||||
// Refresh token otomatis
|
||||
await refreshAccessToken();
|
||||
// Retry request
|
||||
return await next(req);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## 🗃️ DATABASE
|
||||
|
||||
### Layer Abstraksi
|
||||
```go
|
||||
type Driver interface {
|
||||
CreateMemo(ctx context.Context, create *Memo) (*Memo, error)
|
||||
ListMemos(ctx context.Context, find *FindMemo) ([]*Memo, error)
|
||||
UpdateMemo(ctx context.Context, update *UpdateMemo) error
|
||||
DeleteMemo(ctx context.Context, delete *DeleteMemo) error
|
||||
}
|
||||
```
|
||||
|
||||
### Implementasi:
|
||||
- SQLite: `store/db/sqlite/`
|
||||
- MySQL: `store/db/mysql/`
|
||||
- PostgreSQL: `store/db/postgres/`
|
||||
|
||||
### Caching:
|
||||
Menggunakan in-memory cache untuk:
|
||||
- Pengaturan instance
|
||||
- Data user
|
||||
- Pengaturan user
|
||||
|
||||
## 🚀 DEVELOPMENT WORKFLOW
|
||||
|
||||
### Backend Development
|
||||
```bash
|
||||
# Jalankan server development
|
||||
go run ./cmd/memos --port 8081
|
||||
|
||||
# Testing
|
||||
go test ./...
|
||||
|
||||
# Linting
|
||||
golangci-lint run
|
||||
```
|
||||
|
||||
### Frontend Development
|
||||
```bash
|
||||
cd web
|
||||
pnpm install
|
||||
pnpm dev # Server jalan di port 3001, proxy ke 8081
|
||||
```
|
||||
|
||||
### Generate Proto
|
||||
```bash
|
||||
cd proto
|
||||
buf generate
|
||||
```
|
||||
|
||||
## 📦 DEPLOYMENT
|
||||
|
||||
### Opsi Deployment:
|
||||
1. **Docker** (Direkomendasikan)
|
||||
2. **Binary langsung**
|
||||
3. **Docker Compose**
|
||||
4. **Kubernetes**
|
||||
|
||||
### Contoh Docker:
|
||||
```bash
|
||||
docker run -d \
|
||||
--name memos \
|
||||
-p 5230:5230 \
|
||||
-v ~/.memos:/var/opt/memos \
|
||||
neosmemo/memos:stable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Ringkasan Teknologi Utama:**
|
||||
- **Bahasa**: Go + TypeScript
|
||||
- **Framework**: React 18 + Echo (Go)
|
||||
- **API**: gRPC + Connect RPC + REST
|
||||
- **Database**: SQLite/MySQL/PostgreSQL
|
||||
- **Build Tool**: Vite + buf (proto)
|
||||
- **State Management**: React Query + Context
|
||||
- **Styling**: Tailwind CSS
|
||||
Reference in New Issue
Block a user