Files
memos/info.txt
gugus bb402d4ccc
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
first commit
2026-03-04 06:30:47 +00:00

379 lines
9.6 KiB
Plaintext

# 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