Skip to content

Commit 9b25c1d

Browse files
committed
Version 0.2.0
1 parent afb0a88 commit 9b25c1d

151 files changed

Lines changed: 13427 additions & 2824 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursorrules

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# PlainNAS Development Rules
2+
3+
## Project Overview
4+
PlainNAS is a Go-based NAS (Network Attached Storage) system with a Vue 3 web frontend. The backend provides API, file system, media, and watcher services, while the frontend (in `web/`) offers a modern UI for user interaction.
5+
6+
## Architecture & Key Components
7+
8+
### Backend (Go)
9+
- **Entrypoint**: `main.go`, with commands in `cmd/` (e.g., `run.go`, `install.go`)
10+
- **Services**: `internal/services/` (API, watcher), `internal/media/`, `internal/db/`, `internal/cache/`, `internal/config/`
11+
- **GraphQL**: Defined in `internal/graph/schema.graphql`, resolvers in `internal/graph/schema.resolvers.go`, generated code in `internal/graph/generated/`
12+
- **Config**: TOML files in `cmd/install/config.toml`
13+
- **Systemd integration**: `cmd/install/plainnas.service`
14+
15+
### Frontend (Vue 3)
16+
- **Location**: `web/`
17+
- **Main entry**: `web/src/main.ts`, root component: `web/src/App.vue`
18+
- **Components**: `web/src/components/`, assets: `web/src/assets/`
19+
- **Build tools**: Vite (`vite.config.ts`), TypeScript (`tsconfig.json`)
20+
21+
## Developer Workflows
22+
23+
### Install & Initialize
24+
```bash
25+
sudo go run main.go install # Run once to set up packages, DB, config
26+
```
27+
28+
### Run (Dev)
29+
```bash
30+
sudo go run main.go run
31+
```
32+
33+
### GraphQL Codegen
34+
```bash
35+
go env -w GOFLAGS=-mod=mod
36+
go mod tidy
37+
go generate ./internal/graph
38+
```
39+
40+
### Production Build
41+
```bash
42+
go build
43+
sudo mv ./plainnas /usr/local/bin/
44+
sudo systemctl start plainnas
45+
```
46+
47+
### Frontend Build
48+
```bash
49+
cd web/
50+
npm install && npm run build
51+
```
52+
53+
## Logging
54+
55+
Always use the project's logging system located at `internal/pkg/log/log.go`.
56+
57+
### Available Log Functions
58+
59+
```go
60+
import "ismartcoding/plainnas/internal/pkg/log"
61+
62+
// Debug logging
63+
log.Debug("message")
64+
log.Debugf("format %s", value)
65+
66+
// Info logging
67+
log.Info("message")
68+
log.Infof("format %s", value)
69+
70+
// Error logging
71+
log.Error(err)
72+
log.Errorf("format %s", value)
73+
74+
// Trace logging
75+
log.Trace("message")
76+
log.Tracef("format %s", value)
77+
78+
// Panic logging
79+
log.Panic(err)
80+
log.Panicf("format %s", value)
81+
```
82+
83+
### DO NOT Use
84+
85+
❌ `log.Printf()` from standard library
86+
❌ `fmt.Println()` for logging
87+
❌ Any other logging libraries
88+
89+
### Examples
90+
91+
```go
92+
// ✅ Correct
93+
log.Debugf("[FunctionName] operation completed: count=%d", count)
94+
log.Infof("[ModuleName] started processing: id=%s", id)
95+
log.Error(err)
96+
97+
// ❌ Wrong
98+
log.Printf("operation completed: count=%d", count)
99+
fmt.Println("started processing")
100+
```
101+
102+
## Core Development Principles
103+
104+
### Code Quality
105+
- Prioritize **simple, readable, minimal code** (less code is better)
106+
- **Do not reduce features or logic**: behavior must remain correct
107+
- **Do not regress performance**: avoid full scans, avoid N+1 patterns, preserve index-backed fast paths
108+
- **Avoid duplication**: extract shared helpers instead of repeating parsing/filtering/iteration logic
109+
- **Split oversized files**: keep files focused by responsibility (e.g., store vs indexes vs helpers)
110+
111+
### Compatibility & Migration
112+
- **Avoid compatibility/migration/backfill code** unless explicitly requested
113+
- Development workflow: it is acceptable to **delete the DB and rebuild**
114+
- Avoid persistent index version machinery unless explicitly requested
115+
116+
### Documentation
117+
- If logic/behavior changes, update relevant Markdown docs in `README.md` and/or `docs/`
118+
- Keep docs in sync with code
119+
120+
## Platform Support (Linux Only)
121+
122+
PlainNAS is **Linux-only**. Design and implementation should assume Linux (systemd, `/proc`, `/sys`, `mount`, `lsblk`, etc.).
123+
124+
- Do **not** add Windows/macOS fallbacks, multi-platform abstractions, or non-Linux build stubs unless explicitly requested
125+
- It is acceptable for non-Linux builds to fail; correctness on Linux takes priority
126+
127+
## Backend Patterns & Conventions
128+
129+
### Service Boundaries
130+
- API, watcher, media, cache, DB, config are separated by directory
131+
- Always check for service boundaries before making changes
132+
133+
### GraphQL Development
134+
- Schema-driven development: Update `schema.graphql` and run codegen
135+
- Keep `internal/graph/schema.resolvers.go` lightweight
136+
- Resolvers should only validate/translate inputs and delegate to focused files (e.g. `internal/graph/*_api.go`)
137+
- Do not put substantial business logic in `schema.resolvers.go`
138+
139+
### Example: Adding a GraphQL Field
140+
1. Update `internal/graph/schema.graphql`
141+
2. Run `go generate ./internal/graph`
142+
3. Implement resolver in `internal/graph/schema.resolvers.go`
143+
144+
### Error Handling
145+
Always handle errors appropriately:
146+
```go
147+
if err != nil {
148+
log.Error(err)
149+
return nil, err
150+
}
151+
```
152+
153+
### Root-Cause Fixes
154+
- Prefer root-cause fixes over workaround logic
155+
- Avoid extra guards/dedup that mask bugs
156+
- If an invariant is broken, fix the source and let issues surface during development
157+
158+
## Frontend Patterns & Conventions
159+
160+
### Styling Rules
161+
- **Avoid copy-pasting the same scoped CSS** across pages/components
162+
- If a style pattern is used in more than one place, extract it into:
163+
- A shared stylesheet under `web/src/styles/`
164+
- A component-level style
165+
- A small reusable UI component
166+
- Reuse via classes/components instead of duplicating blocks in each `.vue` file
167+
- Keep `scoped` styles for truly view-specific tweaks only
168+
169+
### Forms & Validation (Vue + Vuetify)
170+
- Prefer `vee-validate` `handleSubmit` + schema (see `SettingsBasicView`) for submit-time validation
171+
- Show errors inline via each input's `error`/`error-text` gated by `dirty` and/or `submitAttempted`
172+
- **Do not show extra error toasts** for client-side validation failures when the input already shows an inline error
173+
- Reserve toasts for success and genuine server/network errors
174+
175+
### GraphQL (Vue Apollo)
176+
- Prefer the existing wrappers `initMutation` / `initQuery` / `initLazyQuery` in `web/src/lib/api/*`
177+
- Follow their patterns
178+
179+
#### Mutations (Callback-Driven)
180+
```typescript
181+
// ✅ Correct
182+
const { mutate, loading, onDone, onError } = initMutation({ document: myMutation })
183+
184+
onDone((result) => {
185+
// handle success
186+
})
187+
188+
onError((error) => {
189+
// handle error
190+
})
191+
192+
// ❌ Wrong - do not use try/catch
193+
try {
194+
await mutate()
195+
} catch (error) {
196+
// GraphQL errors are not thrown as exceptions
197+
}
198+
```
199+
200+
#### Queries (Result-Driven)
201+
```typescript
202+
// ✅ Correct
203+
const { loading, onResult } = initQuery({
204+
document: myQuery,
205+
handle: (data, error) => {
206+
if (error) {
207+
// handle error string
208+
} else {
209+
// handle data
210+
}
211+
}
212+
})
213+
214+
// ❌ Wrong - do not use try/catch for GraphQL errors
215+
```
216+
217+
### Yarn Commands
218+
- Yarn v4+ (Berry) does **not** support `yarn -s` / `--silent`
219+
- Run scripts as `yarn <script>` (e.g. `yarn typecheck`) without `-s`
220+
221+
## Testing
222+
223+
- Write unit tests for new functionality
224+
- Use table-driven tests when appropriate
225+
- Mock external dependencies
226+
227+
## Integration Points
228+
229+
- **API**: Go backend exposes GraphQL and REST endpoints (see `internal/services/api/`)
230+
- **Watcher**: Monitors file changes (see `internal/services/watcher/`)
231+
- **Media**: Handles media scanning/storage (see `internal/media/`)
232+
- **Frontend-backend communication**: Via GraphQL and REST APIs
233+
234+
## External Dependencies
235+
236+
- Go modules (see `go.mod`)
237+
- Node.js packages for frontend (see `web/package.json`)
238+
- Systemd for service management
239+
240+
## For AI Agents
241+
242+
- Always check for service boundaries before making changes
243+
- Use provided scripts and commands for setup/builds
244+
- Reference key files for patterns (see above)
245+
- Ask for clarification if workflow or integration is unclear
246+
- Prioritize simple, readable, minimal code
247+
- Avoid unnecessary abstractions and duplication
248+
- Keep documentation in sync with code changes

.github/copilot-instructions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ PlainNAS is a Go-based NAS (Network Attached Storage) system with a Vue 3 web fr
8888
- Prefer index-backed fast paths; avoid full scans and N+1 patterns.
8989
- Prefer root-cause fixes over workaround logic (e.g., extra guards/dedup that mask bugs). If an invariant is broken, fix the source and let issues surface during development.
9090
- Keep `internal/graph/schema.resolvers.go` lightweight: resolvers should only validate/translate inputs and delegate to focused files (e.g. `internal/graph/*_api.go`). Do not put substantial business logic in `schema.resolvers.go`.
91+
- Frontend templates/HTML: keep markup flat and readable—minimize nesting, avoid wrapper `<div>`s unless needed, and do not add `class` attributes unless they are required for styling/layout/testing.
92+
- Do not remove the `<pre class="view-raw">` element; it is intentionally kept for troubleshooting.
9193
- Frontend styling rule: avoid copy-pasting the same scoped CSS across pages/components. If a style pattern is used in more than one place, extract it into an appropriate shared home (a shared stylesheet under `web/src/styles/`, a component-level style, or a small reusable UI component) and reuse it via classes/components instead of duplicating blocks in each `.vue` file. Keep `scoped` styles for truly view-specific tweaks only.
94+
- Prefer SCSS-style nesting for component-local complex popovers and help cards (for readability). When editing a component's `<style scoped>` block, prefer nested selectors (e.g. `.dm-help-pop { .title { ... } .meta { ... } }`) instead of long flattened selectors.
95+
- For small UI popovers (example: disk manager help pop), prefer short, semantic class names: `.title`, `.meta`, `.tip`. Avoid deep BEM-like class trees for ephemeral popper content — prefer simple markup with nested SCSS rules.
9296
- Frontend forms / validation UX (Vue + Vuetify): prefer `vee-validate` `handleSubmit` + schema (see `SettingsBasicView`) for submit-time validation, and show errors inline via each input's `error`/`error-text` gated by `dirty` and/or `submitAttempted`.
9397
- Do not show extra error toasts for client-side validation failures when the input already shows an inline error; reserve toasts for success and genuine server/network errors.
9498
- Frontend GraphQL (Vue Apollo): prefer the existing wrappers `initMutation` / `initQuery` / `initLazyQuery` in `web/src/lib/api/*` and follow their patterns.

.github/workflows/release.yml

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,47 @@ jobs:
2929
- name: Get version info
3030
id: vars
3131
run: |
32-
echo "VERSION=$(grep 'Version[[:space:]]*=' cmd/version.go | awk -F '"' '{print $2}')" >> $GITHUB_OUTPUT
32+
echo "VERSION=$(grep 'Version[[:space:]]*=' internal/version/version.go | awk -F '"' '{print $2}')" >> $GITHUB_OUTPUT
3333
echo "GIT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
3434
echo "BUILD_TIME=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
3535
3636
- name: Build binary
37+
env:
38+
UPDATE_PUBLIC_KEY: ${{ secrets.UPDATE_PUBLIC_KEY }}
3739
run: |
40+
if [ -z "$UPDATE_PUBLIC_KEY" ]; then
41+
echo "missing secret: UPDATE_PUBLIC_KEY" >&2
42+
exit 1
43+
fi
44+
3845
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o plainnas-${{ matrix.goos }}-${{ matrix.goarch }} \
39-
-ldflags "-X 'ismartcoding/plainnas/cmd.BuildTime=${{ steps.vars.outputs.BUILD_TIME }}' -X 'ismartcoding/plainnas/cmd.GitCommit=${{ steps.vars.outputs.GIT_COMMIT }}'"
46+
-ldflags "-X 'ismartcoding/plainnas/internal/version.BuildTime=${{ steps.vars.outputs.BUILD_TIME }}' -X 'ismartcoding/plainnas/internal/version.GitCommit=${{ steps.vars.outputs.GIT_COMMIT }}' -X 'ismartcoding/plainnas/internal/update.DefaultPubKeyB64=${UPDATE_PUBLIC_KEY}'"
47+
48+
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o plainnas-updater-${{ matrix.goos }}-${{ matrix.goarch }} ./cmd/updater
4049
4150
- name: Zip binary
4251
run: |
43-
zip plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip plainnas-${{ matrix.goos }}-${{ matrix.goarch }}
52+
zip plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip \
53+
plainnas-${{ matrix.goos }}-${{ matrix.goarch }} \
54+
plainnas-updater-${{ matrix.goos }}-${{ matrix.goarch }}
55+
56+
- name: Generate sha256 + signature
57+
env:
58+
UPDATE_PRIVATE_KEY: ${{ secrets.UPDATE_PRIVATE_KEY }}
59+
run: |
60+
sha256sum plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip > plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip.sha256
61+
go run ./scripts/sign-ed25519.go \
62+
--in plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip.sha256 \
63+
--out plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip.sha256.sig
4464
4565
- name: Upload artifact
4666
uses: actions/upload-artifact@v4
4767
with:
48-
name: plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip
49-
path: plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip
68+
name: plainnas-${{ matrix.goos }}-${{ matrix.goarch }}
69+
path: |
70+
plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip
71+
plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip.sha256
72+
plainnas-${{ matrix.goos }}-${{ matrix.goarch }}.zip.sha256.sig
5073
5174
release:
5275
needs: build
@@ -65,4 +88,4 @@ jobs:
6588
body: "## What's Changed"
6689
draft: true
6790
prerelease: false
68-
artifacts: ./artifacts/**/plainnas-*.zip
91+
artifacts: ./artifacts/**/plainnas-*.zip,./artifacts/**/plainnas-*.zip.sha256,./artifacts/**/plainnas-*.zip.sha256.sig

0 commit comments

Comments
 (0)