├── .air.toml ├── .dockerignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── labeler.yaml ├── labels.yaml ├── renovate.json5 ├── renovate │ ├── allowedVersions.json5 │ ├── commitMessage.json5 │ ├── labels.json5 │ └── semanticCommits.json5 └── workflows │ ├── ci.yaml │ ├── lint-test-chart.yaml │ ├── meta-label-size.yaml │ ├── meta-labeler.yml │ ├── meta-sync-labels.yaml │ ├── release-chart.yaml │ ├── release.yaml │ ├── schedule-link-checker.yaml │ └── schedule-renovate.yaml ├── .gitignore ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── assets ├── logo-black.svg ├── logo-white.svg ├── logo.png ├── screen01.png ├── screen02.png └── screen03.png ├── charts └── hajimari │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── README.md.gotmpl │ ├── ci │ └── ct-values.yaml │ ├── templates │ ├── NOTES.txt │ ├── app-sample.yaml │ ├── common.yaml │ ├── configmap.yaml │ ├── crd.yaml │ └── rbac.yaml │ └── values.yaml ├── cmd └── hajimari │ └── main.go ├── config.yaml.example ├── frontend ├── .gitignore ├── .npmrc ├── README.md ├── frontend.go ├── package-lock.json ├── package.json ├── src │ ├── app.css │ ├── app.d.ts │ ├── app.html │ ├── lib │ │ ├── AppList │ │ │ ├── AppGroup.svelte │ │ │ └── index.svelte │ │ ├── BookmarkList │ │ │ ├── BookmarkGroup.svelte │ │ │ └── index.svelte │ │ ├── CodeMirror │ │ │ ├── CodeMirror.svelte │ │ │ └── codemirror.ts │ │ ├── Greeting │ │ │ └── Greeting.svelte │ │ ├── Modal │ │ │ └── Modal.svelte │ │ ├── Search │ │ │ └── Search.svelte │ │ ├── ThemeSwitcher │ │ │ └── ThemeSwitcher.svelte │ │ ├── api.ts │ │ └── stores.ts │ └── routes │ │ ├── +error.svelte │ │ ├── +layout.svelte │ │ └── [...slug] │ │ ├── +page.svelte │ │ └── +page.ts ├── static │ └── favicon.png ├── svelte.config.js ├── tsconfig.json └── vite.config.ts ├── go.mod ├── go.sum └── internal ├── annotations └── annotations.go ├── config └── config.go ├── hajimari ├── crdapps │ └── apps.go ├── customapps │ └── customapps.go └── ingressapps │ ├── apps.go │ └── filters.go ├── handlers ├── apps.go ├── bookmarks.go ├── errors.go ├── handler.go ├── notfound.go └── startpage.go ├── kube ├── client.go ├── lists │ ├── crdapps │ │ └── crdapps.go │ └── ingresses │ │ └── ingresses.go ├── types │ └── v1alpha1 │ │ └── types.go ├── util │ ├── labelselector.go │ ├── namespaces.go │ └── replicastatusgetter.go └── wrappers │ ├── app.go │ ├── helpers.go │ └── ingress.go ├── log └── log.go ├── models ├── app.go ├── app_group.go ├── bookmark.go ├── bookmark_group.go ├── replica_info.go ├── search_provider.go ├── startpage.go └── theme.go ├── services ├── app.go └── startpage.go ├── stores ├── file.go ├── memory.go └── startpage.go └── util ├── pointer └── pointer.go └── strings └── strings.go /.air.toml: -------------------------------------------------------------------------------- 1 | root = "." 2 | tmp_dir = "tmp" 3 | 4 | [build] 5 | full_bin = "MEMORY=true LOG_LEVEL=debug ./tmp/hajimari" 6 | cmd = "go build -o ./tmp/hajimari ./cmd/hajimari/main.go" 7 | delay = 1000 8 | exclude_dir = ["frontend", "docs", "charts", "tmp", "vendor"] 9 | exclude_file = [] 10 | exclude_regex = [] 11 | exclude_unchanged = false 12 | follow_symlink = false 13 | include_dir = [] 14 | include_ext = ["go", "tpl", "tmpl", "html"] 15 | kill_delay = "0s" 16 | log = "build-errors.log" 17 | send_interrupt = false 18 | stop_on_error = true 19 | 20 | [color] 21 | app = "" 22 | build = "yellow" 23 | main = "magenta" 24 | runner = "green" 25 | watcher = "cyan" 26 | 27 | [log] 28 | time = false 29 | 30 | [misc] 31 | clean_on_exit = true 32 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github/ 3 | .vscode/ 4 | .gitignore 5 | .kubeconfig 6 | assets/ 7 | charts/ 8 | data/ 9 | docs/ 10 | Dockerfile 11 | LICENSE 12 | README.md 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | * @toboshii 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: kind/bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: Discuss on Discord 5 | url: https://discord.gg/HWGZSWJsA8 6 | about: Join our Discord community 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: kind/enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description of the change** 2 | 3 | 4 | 5 | **Benefits** 6 | 7 | 8 | 9 | **Possible drawbacks** 10 | 11 | 12 | 13 | **Applicable issues** 14 | 15 | 16 | - fixes # 17 | 18 | **Additional information** 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | area/ansible: 3 | - "ansible/**/*" 4 | area/github: 5 | - ".github/**/*" 6 | area/cluster: 7 | - "cluster/**/*" 8 | area/hack: 9 | - "hack/**/*" 10 | area/terraform: 11 | - "terraform/**/*" 12 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Area 3 | - name: area/ansible 4 | color: "72ccf3" 5 | description: >- 6 | Changes made in the ansible directory 7 | - name: area/github 8 | color: "72ccf3" 9 | description: >- 10 | Changes made in the github directory 11 | - name: area/cluster 12 | color: "72ccf3" 13 | description: >- 14 | Changes made in the cluster directory 15 | - name: area/hack 16 | color: "72ccf3" 17 | description: >- 18 | Changes made in the hack directory 19 | - name: area/terraform 20 | color: "72ccf3" 21 | description: >- 22 | Changes made in the terraform directory 23 | # Renovate 24 | - name: renovate/ansible 25 | color: "ffc300" 26 | - name: renovate/container 27 | color: "ffc300" 28 | - name: renovate/github-action 29 | color: "ffc300" 30 | - name: renovate/github-release 31 | color: "ffc300" 32 | - name: renovate/helm 33 | color: "ffc300" 34 | - name: renovate/terraform 35 | color: "ffc300" 36 | # Semantic Type 37 | - name: type/patch 38 | color: "FFEC19" 39 | - name: type/minor 40 | color: "FF9800" 41 | - name: type/major 42 | color: "F6412D" 43 | # Size 44 | - name: size/XS 45 | color: "009900" 46 | description: >- 47 | Denotes a PR that changes 0-9 lines, ignoring generated files. 48 | - name: size/S 49 | color: "77bb00" 50 | description: >- 51 | Denotes a PR that changes 10-29 lines, ignoring generated files. 52 | - name: size/M 53 | color: "eebb00" 54 | description: >- 55 | Denotes a PR that changes 30-99 lines, ignoring generated files. 56 | - name: size/L 57 | color: "ee9900" 58 | description: >- 59 | Denotes a PR that changes 100-499 lines, ignoring generated files. 60 | - name: size/XL 61 | color: "ee5500" 62 | description: >- 63 | Denotes a PR that changes 500-999 lines, ignoring generated files. 64 | - name: size/XXL 65 | color: "ee0000" 66 | description: >- 67 | Denotes a PR that changes 1000+ lines, ignoring generated files. 68 | # Uncategorized 69 | - name: bug 70 | color: "ee0701" 71 | - name: do-not-merge 72 | color: "ee0701" 73 | - name: docs 74 | color: "F4D1B7" 75 | - name: enhancement 76 | color: "84b6eb" 77 | - name: link-checker 78 | color: "7B55D7" 79 | - name: question 80 | color: "cc317c" 81 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | "docker:enableMajor", 5 | ":disableRateLimiting", 6 | ":dependencyDashboard", 7 | ":semanticCommits", 8 | ":enablePreCommit", 9 | ":automergeDigest", 10 | ":automergeBranchPush", 11 | "github>toboshii/home-ops//.github/renovate/commitMessage.json5", 12 | "github>toboshii/home-ops//.github/renovate/labels.json5", 13 | "github>toboshii/home-ops//.github/renovate/semanticCommits.json5", 14 | "github>toboshii/home-ops//.github/renovate/allowedVersions.json5" 15 | ], 16 | "platform": "github", 17 | "username": "chii-bot[bot]", 18 | "repositories": ["toboshii/hajimari"], 19 | "onboarding": false, 20 | "requireConfig": false, 21 | "gitAuthor": "chii-bot <109454249+chii-bot[bot]@users.noreply.github.com>", 22 | "dependencyDashboardTitle": "Renovate Dashboard 🤖", 23 | "suppressNotifications": ["prIgnoreNotification"], 24 | "rebaseWhen": "conflicted", 25 | "commitBodyTable": true, 26 | } 27 | -------------------------------------------------------------------------------- /.github/renovate/allowedVersions.json5: -------------------------------------------------------------------------------- 1 | { 2 | "packageRules": [ 3 | { 4 | "matchPackageNames": ["k8s.io/client-go"], 5 | "allowedVersions": "!/1\\.(4\\.0|5\\.0|5\\.1|5\\.2)$/" 6 | }, 7 | { 8 | "matchPackageNames": ["codemirror"], 9 | "allowedVersions": "< 6.0.0" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /.github/renovate/commitMessage.json5: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessageTopic": "{{depName}}", 3 | "commitMessageExtra": "to {{newVersion}}", 4 | "commitMessageSuffix": "", 5 | "packageRules": [ 6 | { 7 | "matchDatasources": ["helm"], 8 | "commitMessageTopic": "chart {{depName}}" 9 | }, 10 | { 11 | "matchDatasources": ["docker"], 12 | "commitMessageTopic": "image {{depName}}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.github/renovate/labels.json5: -------------------------------------------------------------------------------- 1 | { 2 | "packageRules": [ 3 | { 4 | "matchUpdateTypes": ["major"], 5 | "labels": ["type/major"] 6 | }, 7 | { 8 | "matchUpdateTypes": ["minor"], 9 | "labels": ["type/minor"] 10 | }, 11 | { 12 | "matchUpdateTypes": ["patch"], 13 | "labels": ["type/patch"] 14 | }, 15 | { 16 | "matchDatasources": ["docker"], 17 | "addLabels": ["renovate/container"] 18 | }, 19 | { 20 | "matchDatasources": ["helm"], 21 | "addLabels": ["renovate/helm"] 22 | }, 23 | { 24 | "matchDatasources": ["go", "golang-version"], 25 | "addLabels": ["renovate/go"] 26 | }, 27 | { 28 | "matchDatasources": ["npm"], 29 | "addLabels": ["renovate/js"] 30 | }, 31 | { 32 | "matchDatasources": ["github-releases", "github-tags"], 33 | "addLabels": ["renovate/github-release"] 34 | }, 35 | { 36 | "matchManagers": ["github-actions"], 37 | "addLabels": ["renovate/github-action"] 38 | }, 39 | { 40 | "matchDatasources": ["pypi"], 41 | "addLabels": ["renovate/pip"] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.github/renovate/semanticCommits.json5: -------------------------------------------------------------------------------- 1 | { 2 | "packageRules": [ 3 | { 4 | "matchDatasources": ["go", "golang-version"], 5 | "matchUpdateTypes": ["major"], 6 | "commitMessagePrefix": "feat(go)!: " 7 | }, 8 | { 9 | "matchDatasources": ["go", "golang-version"], 10 | "matchUpdateTypes": ["minor"], 11 | "semanticCommitType": "feat", 12 | "semanticCommitScope": "go" 13 | }, 14 | { 15 | "matchDatasources": ["go", "golang-version"], 16 | "matchUpdateTypes": ["patch"], 17 | "semanticCommitType": "fix", 18 | "semanticCommitScope": "go" 19 | }, 20 | { 21 | "matchDatasources": ["npm"], 22 | "matchUpdateTypes": ["major"], 23 | "commitMessagePrefix": "feat(js)!: " 24 | }, 25 | { 26 | "matchDatasources": ["npm"], 27 | "matchUpdateTypes": ["minor"], 28 | "semanticCommitType": "feat", 29 | "semanticCommitScope": "js" 30 | }, 31 | { 32 | "matchDatasources": ["npm"], 33 | "matchUpdateTypes": ["patch"], 34 | "semanticCommitType": "fix", 35 | "semanticCommitScope": "js" 36 | }, 37 | { 38 | "matchDatasources": ["docker"], 39 | "matchUpdateTypes": ["major"], 40 | "commitMessagePrefix": "feat(container)!: " 41 | }, 42 | { 43 | "matchDatasources": ["docker"], 44 | "matchUpdateTypes": ["minor"], 45 | "semanticCommitType": "feat", 46 | "semanticCommitScope": "container" 47 | }, 48 | { 49 | "matchDatasources": ["docker"], 50 | "matchUpdateTypes": ["digest", "patch"], 51 | "semanticCommitType": "fix", 52 | "semanticCommitScope": "container" 53 | }, 54 | { 55 | "matchDatasources": ["github-releases", "github-tags"], 56 | "matchUpdateTypes": ["major"], 57 | "commitMessagePrefix": "feat(github-release)!: " 58 | }, 59 | { 60 | "matchDatasources": ["github-releases", "github-tags"], 61 | "matchUpdateTypes": ["minor"], 62 | "semanticCommitType": "feat", 63 | "semanticCommitScope": "github-release" 64 | }, 65 | { 66 | "matchDatasources": ["github-releases", "github-tags"], 67 | "matchUpdateTypes": ["patch"], 68 | "semanticCommitType": "fix", 69 | "semanticCommitScope": "github-release" 70 | }, 71 | { 72 | "matchManagers": ["github-actions"], 73 | "matchUpdateTypes": ["major"], 74 | "commitMessagePrefix": "feat(github-action)!: " 75 | }, 76 | { 77 | "matchManagers": ["github-actions"], 78 | "matchUpdateTypes": ["minor"], 79 | "semanticCommitType": "feat", 80 | "semanticCommitScope": "github-action" 81 | }, 82 | { 83 | "matchManagers": ["github-actions"], 84 | "matchUpdateTypes": ["patch"], 85 | "semanticCommitType": "fix", 86 | "semanticCommitScope": "github-action" 87 | }, 88 | { 89 | "matchDatasources": ["pypi"], 90 | "matchUpdateTypes": ["major"], 91 | "commitMessagePrefix": "feat(pip)!: " 92 | }, 93 | { 94 | "matchDatasources": ["pypi"], 95 | "matchUpdateTypes": ["minor"], 96 | "semanticCommitType": "feat", 97 | "semanticCommitScope": "pip" 98 | }, 99 | { 100 | "matchDatasources": ["pypi"], 101 | "matchUpdateTypes": ["patch"], 102 | "semanticCommitType": "fix", 103 | "semanticCommitScope": "pip" 104 | } 105 | ] 106 | } 107 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - "charts/hajimari/**" 9 | 10 | env: 11 | REGISTRY_IMAGE: ghcr.io/toboshii/hajimari 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-20.04 16 | if: "!contains(github.event.head_commit.message, '[ci-skip]')" 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | - name: Prepare 21 | id: prep 22 | run: | 23 | echo ::set-output name=version::${GITHUB_REF##*/}-$(git rev-parse --short HEAD) 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v2 26 | with: 27 | platforms: all 28 | - name: Set up Docker Buildx 29 | id: buildx 30 | uses: docker/setup-buildx-action@v2 31 | with: 32 | install: true 33 | version: latest 34 | driver-opts: image=moby/buildkit:master 35 | - name: Login to GitHub Container Registry 36 | uses: docker/login-action@v2 37 | with: 38 | registry: ghcr.io 39 | username: ${{ github.actor }} 40 | password: ${{ secrets.GITHUB_TOKEN }} 41 | - name: Build and Push 42 | uses: docker/build-push-action@v4 43 | with: 44 | context: . 45 | file: ./Dockerfile 46 | platforms: linux/amd64,linux/arm64 47 | push: true 48 | tags: | 49 | ${{ env.REGISTRY_IMAGE }}:${{ steps.prep.outputs.version }} 50 | -------------------------------------------------------------------------------- /.github/workflows/lint-test-chart.yaml: -------------------------------------------------------------------------------- 1 | name: Lint and Test Chart 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "charts/hajimari/**" 7 | 8 | jobs: 9 | lint-test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Set up Helm 18 | uses: azure/setup-helm@v3 19 | with: 20 | version: v3.6.3 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: 3.7 26 | 27 | - name: Set up chart-testing 28 | uses: helm/chart-testing-action@v2.3.1 29 | 30 | - name: Run chart-testing (list-changed) 31 | id: list-changed 32 | run: | 33 | changed=$(ct list-changed) 34 | if [[ -n "$changed" ]]; then 35 | echo "::set-output name=changed::true" 36 | fi 37 | 38 | - name: Run chart-testing (lint) 39 | run: ct lint 40 | 41 | - name: Create Kind cluster 42 | uses: helm/kind-action@v1.5.0 43 | with: 44 | wait: 120s 45 | if: steps.list-changed.outputs.changed == 'true' 46 | 47 | - name: Run chart-testing (install) 48 | run: ct install -------------------------------------------------------------------------------- /.github/workflows/meta-label-size.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Meta - Label Size 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | label-size: 11 | name: Label Size 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Generate Token 15 | uses: tibdex/github-app-token@v1 16 | id: generate-token 17 | with: 18 | app_id: "${{ secrets.BOT_APP_ID }}" 19 | private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 20 | - name: Label Size 21 | uses: pascalgn/size-label-action@v0.4.3 22 | env: 23 | GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}" 24 | with: 25 | sizes: > 26 | { 27 | "0": "XS", 28 | "20": "S", 29 | "50": "M", 30 | "200": "L", 31 | "800": "XL", 32 | "2000": "XXL" 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/meta-labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Meta - Labeler 3 | 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | labeler: 11 | name: Labeler 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Generate Token 15 | uses: tibdex/github-app-token@v1 16 | id: generate-token 17 | with: 18 | app_id: "${{ secrets.BOT_APP_ID }}" 19 | private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 20 | - name: Labeler 21 | uses: actions/labeler@v4 22 | with: 23 | configuration-path: .github/labeler.yaml 24 | repo-token: "${{ steps.generate-token.outputs.token }}" 25 | -------------------------------------------------------------------------------- /.github/workflows/meta-sync-labels.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Meta - Sync labels 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - main 9 | paths: 10 | - ".github/labels.yaml" 11 | 12 | jobs: 13 | labels: 14 | name: Sync Labels 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | - name: Generate Token 20 | uses: tibdex/github-app-token@v1 21 | id: generate-token 22 | with: 23 | app_id: "${{ secrets.BOT_APP_ID }}" 24 | private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 25 | - name: Sync Labels 26 | uses: EndBug/label-sync@v2 27 | with: 28 | config-file: .github/labels.yaml 29 | token: "${{ steps.generate-token.outputs.token }}" 30 | delete-other-labels: true 31 | -------------------------------------------------------------------------------- /.github/workflows/release-chart.yaml: -------------------------------------------------------------------------------- 1 | name: release chart 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "charts/hajimari/**" 9 | 10 | jobs: 11 | release: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Configure Git 20 | run: | 21 | git config user.name "$GITHUB_ACTOR" 22 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 23 | 24 | - name: Install Helm 25 | uses: azure/setup-helm@v3 26 | with: 27 | version: v3.6.3 28 | 29 | - name: Run chart-releaser 30 | uses: helm/chart-releaser-action@v1.5.0 31 | env: 32 | CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 33 | CR_RELEASE_NAME_TEMPLATE: "hajimari-helm-chart-{{ .Version }}" 34 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: 7 | - published 8 | 9 | env: 10 | REGISTRY_IMAGE: ghcr.io/toboshii/hajimari 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-20.04 15 | if: "!contains(github.event.head_commit.message, '[ci-skip]')" 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Prepare 21 | id: prep 22 | run: | 23 | echo ::set-output name=version::${GITHUB_REF##*/} 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v2 26 | with: 27 | platforms: all 28 | - name: Set up Docker Buildx 29 | id: buildx 30 | uses: docker/setup-buildx-action@v2 31 | with: 32 | install: true 33 | version: latest 34 | driver-opts: image=moby/buildkit:master 35 | - name: Login to GitHub Container Registry 36 | if: github.event_name != 'pull_request' 37 | uses: docker/login-action@v2 38 | with: 39 | registry: ghcr.io 40 | username: ${{ github.actor }} 41 | password: ${{ secrets.GITHUB_TOKEN }} 42 | - name: Build and Push 43 | if: github.event_name != 'pull_request' 44 | uses: docker/build-push-action@v4 45 | with: 46 | context: . 47 | file: ./Dockerfile 48 | platforms: linux/amd64,linux/arm64 49 | push: true 50 | tags: | 51 | ${{ env.REGISTRY_IMAGE }}:latest 52 | ${{ env.REGISTRY_IMAGE }}:${{ steps.prep.outputs.version }} 53 | -------------------------------------------------------------------------------- /.github/workflows/schedule-link-checker.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Schedule - Link Checker 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | jobs: 10 | link-checker: 11 | name: Link Checker 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Generate Token 17 | uses: tibdex/github-app-token@v1 18 | id: generate-token 19 | with: 20 | app_id: "${{ secrets.BOT_APP_ID }}" 21 | private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 22 | - name: Link Checker 23 | uses: lycheeverse/lychee-action@v1.6.1 24 | id: lychee 25 | env: 26 | GITHUB_TOKEN: "${{ steps.generate-token.outputs.token }}" 27 | - name: Find Link Checker Issue 28 | id: link-checker-issue 29 | uses: micalevisk/last-issue-action@v2 30 | with: 31 | state: open 32 | labels: | 33 | link-checker 34 | - name: Update Issue 35 | uses: peter-evans/create-issue-from-file@v4 36 | with: 37 | title: Broken links detected in docs 🔗 38 | issue-number: "${{ steps.link-checker-issue.outputs.issue-number }}" 39 | content-filepath: ./lychee/out.md 40 | token: "${{ steps.generate-token.outputs.token }}" 41 | labels: | 42 | link-checker 43 | -------------------------------------------------------------------------------- /.github/workflows/schedule-renovate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Schedule - Renovate 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | inputs: 7 | dryRun: 8 | description: "Dry-Run" 9 | default: "false" 10 | required: false 11 | logLevel: 12 | description: "Log-Level" 13 | default: "debug" 14 | required: false 15 | schedule: 16 | - cron: "0 * * * *" 17 | push: 18 | branches: 19 | - main 20 | paths: 21 | - ".github/renovate.json5" 22 | - ".github/renovate/**.json5" 23 | 24 | env: 25 | LOG_LEVEL: debug 26 | DRY_RUN: false 27 | RENOVATE_CONFIG_FILE: .github/renovate.json5 28 | 29 | jobs: 30 | renovate: 31 | name: Renovate 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v3 36 | - name: Generate Token 37 | uses: tibdex/github-app-token@v1 38 | id: generate-token 39 | with: 40 | app_id: "${{ secrets.BOT_APP_ID }}" 41 | private_key: "${{ secrets.BOT_APP_PRIVATE_KEY }}" 42 | - name: Override default config from dispatch variables 43 | run: | 44 | echo "DRY_RUN=${{ github.event.inputs.dryRun || env.DRY_RUN }}" >> "${GITHUB_ENV}" 45 | echo "LOG_LEVEL=${{ github.event.inputs.logLevel || env.LOG_LEVEL }}" >> "${GITHUB_ENV}" 46 | - name: Renovate 47 | uses: renovatebot/github-action@v36.0.2 48 | with: 49 | configurationFile: "${{ env.RENOVATE_CONFIG_FILE }}" 50 | token: "x-access-token:${{ steps.generate-token.outputs.token }}" 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | tmp 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Project specific 16 | /data 17 | /config.yaml 18 | /hajimari 19 | /.kubeconfig 20 | /bin 21 | 22 | # Chart dependencies 23 | charts/hajimari/charts 24 | charts/hajimari/Chart.lock 25 | charts/hajimari/local.yaml 26 | 27 | # UI dependencies 28 | node_modules 29 | public/build/ 30 | 31 | .DS_Store -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "svelte.plugin.svelte.compilerWarnings": { 3 | "a11y-autofocus": "ignore" 4 | } 5 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/node:16.20-alpine AS build-frontend 2 | 3 | WORKDIR /build 4 | 5 | COPY . . 6 | 7 | WORKDIR /build/frontend 8 | 9 | RUN npm install 10 | 11 | RUN npm run build 12 | 13 | FROM docker.io/golang:1.20.2-alpine as build 14 | 15 | ARG TARGETPLATFORM 16 | ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64} 17 | 18 | ENV GO111MODULE=on \ 19 | CGO_ENABLED=0 20 | 21 | WORKDIR /build 22 | 23 | COPY . . 24 | 25 | COPY --from=build-frontend /build/frontend/build /build/frontend/build 26 | 27 | RUN \ 28 | export GOOS=$(echo ${TARGETPLATFORM} | cut -d / -f1) \ 29 | && \ 30 | export GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \ 31 | && \ 32 | GOARM=$(echo ${TARGETPLATFORM} | cut -d / -f3); export GOARM=${GOARM:1} \ 33 | && \ 34 | go mod download \ 35 | && \ 36 | go build -a -tags netgo -ldflags '-w -extldflags "-static"' -o hajimari /build/cmd/hajimari/main.go \ 37 | && \ 38 | chmod +x hajimari 39 | 40 | FROM docker.io/alpine:3.17 41 | 42 | RUN \ 43 | apk add --no-cache \ 44 | tzdata \ 45 | tini \ 46 | && \ 47 | addgroup -S hajimari \ 48 | && \ 49 | adduser -S hajimari -G hajimari 50 | 51 | COPY --from=build /build/hajimari /usr/local/bin/hajimari 52 | 53 | USER hajimari:hajimari 54 | ENTRYPOINT [ "/sbin/tini", "--" ] 55 | CMD [ "hajimari" ] 56 | 57 | LABEL org.opencontainers.image.source https://github.com/toboshii/hajimari 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | .PHONY: help 4 | # From: http://disq.us/p/16327nq 5 | help: ## This help. 6 | @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) 7 | 8 | .PHONY: deps 9 | deps: ## Install required dependencies 10 | cd frontend/ && npm install 11 | 12 | .PHONY: build 13 | build: ## Build the project 14 | npm run build --prefix frontend/ 15 | go build -ldflags="-s -w" -trimpath -o ./bin/hajimari ./cmd/hajimari/main.go 16 | 17 | .PHONY: run 18 | run: build ## Run the program 19 | ./bin/hajimari 20 | 21 | .PHONY: dev 22 | dev: ## Run the frontend & backend in dev mode 23 | make -j 2 dev-backend dev-frontend 24 | 25 | .PHONY: dev-backend 26 | dev-backend: ## Run the backend in dev mode 27 | air & 28 | 29 | .PHONY: dev-frontend 30 | dev-frontend: ## Run the frontend in dev mode 31 | cd frontend/ && sleep 3 && npm run dev -- --open 32 | 33 | .PHONY: fmt 34 | fmt: ## Format the project with gofmt 35 | gofmt -l -w -s . 36 | 37 | .PHONY: lint 38 | lint: ## Lint code with golangci-lint 39 | golangci-lint run 40 | 41 | .PHONY: test 42 | test: ## Run the tests 43 | go test -cover ./... -------------------------------------------------------------------------------- /assets/logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 31 | 34 | 36 | 39 | 42 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 60 | 63 | 65 | 68 | 69 | 70 | 73 | 75 | 78 | 81 | 84 | 87 | 89 | 92 | 95 | 98 | 101 | 103 | 106 | 109 | 112 | 115 | 117 | 119 | 121 | 124 | 126 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toboshii/hajimari/b07024d997942dcd97a533e89a92619d5a9cdd98/assets/logo.png -------------------------------------------------------------------------------- /assets/screen01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toboshii/hajimari/b07024d997942dcd97a533e89a92619d5a9cdd98/assets/screen01.png -------------------------------------------------------------------------------- /assets/screen02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toboshii/hajimari/b07024d997942dcd97a533e89a92619d5a9cdd98/assets/screen02.png -------------------------------------------------------------------------------- /assets/screen03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toboshii/hajimari/b07024d997942dcd97a533e89a92619d5a9cdd98/assets/screen03.png -------------------------------------------------------------------------------- /charts/hajimari/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | # OWNERS file for Kubernetes 24 | OWNERS 25 | # helm-docs templates 26 | *.gotmpl -------------------------------------------------------------------------------- /charts/hajimari/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: v0.3.1 3 | description: | 4 | Hajimari is a beautiful & customizable browser startpage/dashboard with 5 | Kubernetes application discovery 6 | name: hajimari 7 | version: 2.0.2 8 | kubeVersion: ">=1.16.0-0" 9 | keywords: 10 | - hajimari 11 | - startpage 12 | - dashboard 13 | home: https://github.com/toboshii/hajimari/tree/master/charts/hajimari 14 | icon: https://raw.githubusercontent.com/toboshii/hajimari/main/assets/logo.png 15 | sources: 16 | - https://github.com/toboshii/hajimari 17 | maintainers: 18 | - name: toboshii 19 | email: toboshii@users.noreply.github.com 20 | dependencies: 21 | - name: common 22 | version: 0.2.2 23 | repository: https://bjw-s.github.io/helm-charts/ -------------------------------------------------------------------------------- /charts/hajimari/README.md: -------------------------------------------------------------------------------- 1 | # hajimari 2 | 3 | ![Version: 2.0.2](https://img.shields.io/badge/Version-2.0.2-informational?style=flat-square) ![AppVersion: v0.3.1](https://img.shields.io/badge/AppVersion-v0.3.1-informational?style=flat-square) 4 | 5 | Hajimari is a beautiful & customizable browser startpage/dashboard with 6 | Kubernetes application discovery 7 | 8 | **Homepage:** 9 | 10 | ## Maintainers 11 | 12 | | Name | Email | Url | 13 | | ---- | ------ | --- | 14 | | toboshii | | | 15 | 16 | ## Example values to get started 17 | 18 | ```yaml 19 | hajimari: 20 | instanceName: null 21 | defaultEnable: true 22 | namespaceSelector: 23 | matchNames: 24 | - default 25 | - downloads 26 | - media 27 | name: You 28 | title: Hajimari 29 | lightTheme: gazette 30 | darkTheme: horizon 31 | customThemes: 32 | - name: danger 33 | backgroundColor: '#0c0e0c' 34 | primaryColor: '#eaebea' 35 | accentColor: '#d8323c' 36 | showGreeting: true 37 | showAppGroups: false 38 | showAppUrls: true 39 | showAppInfo: false 40 | showAppStatus: true 41 | defaultAppIcon: mdi:application 42 | showBookmarkGroups: true 43 | showGlobalBookmarks: false 44 | alwaysTargetBlank: false 45 | defaultSearchProvider: Google 46 | searchProviders: 47 | - name: Google 48 | token: g 49 | icon: simple-icons:google 50 | searchUrl: https://www.google.com/search?q={query} 51 | url: https://www.google.com 52 | - name: DuckDuckGo 53 | token: d 54 | icon: simple-icons:duckduckgo 55 | searchUrl: https://duckduckgo.com/?q={query} 56 | url: https://duckduckgo.com 57 | - name: IMDB 58 | token: i 59 | icon: simple-icons:imdb 60 | searchUrl: https://www.imdb.com/find?q={query} 61 | url: https://www.imdb.com 62 | - name: Reddit 63 | token: r 64 | icon: simple-icons:reddit 65 | searchUrl: https://www.reddit.com/search?q={query} 66 | url: https://www.reddit.com 67 | - name: YouTube 68 | token: 'y' 69 | icon: simple-icons:youtube 70 | searchUrl: https://www.youtube.com/results?search_query={query} 71 | url: https://www.youtube.com 72 | - name: Spotify 73 | token: s 74 | icon: simple-icons:spotify 75 | searchUrl: hhttps://open.spotify.com/search/{query} 76 | url: https://open.spotify.com 77 | customApps: 78 | - group: Media 79 | apps: 80 | - name: Test 81 | url: 'https://example.com' 82 | icon: 'mdi:test-tube' 83 | info: This is a test app 84 | globalBookmarks: 85 | - group: Communicate 86 | bookmarks: 87 | - name: Discord 88 | url: 'https://discord.com' 89 | - name: Gmail 90 | url: 'http://gmail.com' 91 | - name: Slack 92 | url: 'https://slack.com/signin' 93 | - group: Cloud 94 | bookmarks: 95 | - name: Box 96 | url: 'https://box.com' 97 | - name: Dropbox 98 | url: 'https://dropbox.com' 99 | - name: Drive 100 | url: 'https://drive.google.com' 101 | - group: Design 102 | bookmarks: 103 | - name: Awwwards 104 | url: 'https://awwwards.com' 105 | - name: Dribbble 106 | url: 'https://dribbble.com' 107 | - name: Muz.li 108 | url: 'https://medium.muz.li/' 109 | - group: Dev 110 | bookmarks: 111 | - name: Codepen 112 | url: 'https://codepen.io/' 113 | - name: Devdocs 114 | url: 'https://devdocs.io' 115 | - name: Devhints 116 | url: 'https://devhints.io' 117 | - group: Lifestyle 118 | bookmarks: 119 | - name: Design Milk 120 | url: 'https://design-milk.com/category/interior-design/' 121 | - name: Dwell 122 | url: 'https://www.dwell.com/' 123 | - name: Freshome 124 | url: 'https://www.mymove.com/freshome/' 125 | - group: Media 126 | bookmarks: 127 | - name: Spotify 128 | url: 'http://browse.spotify.com' 129 | - name: Trakt 130 | url: 'http://trakt.tv' 131 | - name: YouTube 132 | url: 'https://youtube.com/feed/subscriptions' 133 | - group: Reading 134 | bookmarks: 135 | - name: Instapaper 136 | url: 'https://www.instapaper.com/u' 137 | - name: Medium 138 | url: 'http://medium.com' 139 | - name: Reddit 140 | url: 'http://reddit.com' 141 | - group: Tech 142 | bookmarks: 143 | - name: TheNextWeb 144 | url: 'https://thenextweb.com/' 145 | - name: The Verge 146 | url: 'https://theverge.com/' 147 | - name: MIT Technology Review 148 | url: 'https://www.technologyreview.com/' 149 | ingress: 150 | main: 151 | enabled: true 152 | hosts: 153 | - host: hajimari.domain.tld 154 | paths: 155 | - path: / 156 | pathType: Prefix 157 | persistence: 158 | data: 159 | enabled: true 160 | accessMode: ReadWriteOnce 161 | size: 1Gi 162 | ``` 163 | 164 | ## Source Code 165 | 166 | * 167 | 168 | ## Requirements 169 | 170 | Kubernetes: `>=1.16.0-0` 171 | 172 | | Repository | Name | Version | 173 | |------------|------|---------| 174 | | https://bjw-s.github.io/helm-charts/ | common | 0.2.2 | 175 | 176 | ## Values 177 | 178 | | Key | Type | Default | Description | 179 | |-----|------|---------|-------------| 180 | | env | object | See below | environment variables. | 181 | | env.TZ | string | `"UTC"` | Set the container timezone | 182 | | hajimari | object | See below | Configures Hajimari settings for this instance. | 183 | | hajimari.customApps | list | `[]` | Add custom applications to the discovered application list | 184 | | hajimari.defaultEnable | bool | `false` | Set to true to show all discovered applications by default. | 185 | | hajimari.globalBookmarks | list | `[]` | Set default bookmarks | 186 | | hajimari.instanceName | string | `nil` | The name of this instance, this allows running multiple instances of Hajimari on the same cluster | 187 | | hajimari.name | string | `"You"` | Default name for welcome message | 188 | | hajimari.namespaceSelector | object | `{"matchNames":["media"]}` | Namespace selector to use for discovering applications | 189 | | hajimari.title | string | `nil` | Override the title of the Hajimari pages | 190 | | image.pullPolicy | string | `"IfNotPresent"` | image pull policy | 191 | | image.repository | string | `"ghcr.io/toboshii/hajimari"` | image repository | 192 | | image.tag | string | `"v0.3.1"` | image tag | 193 | | ingress.main | object | See values.yaml | Enable and configure ingress settings for the chart under this key. | 194 | | persistence | object | See values.yaml | Configure persistence settings for the chart under this key. | 195 | | service | object | See values.yaml | Configures service settings for the chart. | 196 | | serviceAccount | object | See below | Configures service account needed for reading k8s ingress objects | 197 | | serviceAccount.create | bool | `true` | Create service account | 198 | -------------------------------------------------------------------------------- /charts/hajimari/README.md.gotmpl: -------------------------------------------------------------------------------- 1 | {{ template "chart.header" . }} 2 | {{ template "chart.deprecationWarning" . }} 3 | 4 | {{ template "chart.badgesSection" . }} 5 | 6 | {{ template "chart.description" . }} 7 | 8 | {{ template "chart.homepageLine" . }} 9 | 10 | {{ template "chart.maintainersSection" . }} 11 | 12 | ## Example values to get started 13 | 14 | ```yaml 15 | hajimari: 16 | instanceName: null 17 | defaultEnable: true 18 | namespaceSelector: 19 | matchNames: 20 | - default 21 | - downloads 22 | - media 23 | name: You 24 | title: Hajimari 25 | lightTheme: gazette 26 | darkTheme: horizon 27 | customThemes: 28 | - name: danger 29 | backgroundColor: '#0c0e0c' 30 | primaryColor: '#eaebea' 31 | accentColor: '#d8323c' 32 | showGreeting: true 33 | showAppGroups: false 34 | showAppUrls: true 35 | showAppInfo: false 36 | showAppStatus: true 37 | defaultAppIcon: mdi:application 38 | showBookmarkGroups: true 39 | showGlobalBookmarks: false 40 | alwaysTargetBlank: false 41 | defaultSearchProvider: Google 42 | searchProviders: 43 | - name: Google 44 | token: g 45 | icon: simple-icons:google 46 | searchUrl: https://www.google.com/search?q={query} 47 | url: https://www.google.com 48 | - name: DuckDuckGo 49 | token: d 50 | icon: simple-icons:duckduckgo 51 | searchUrl: https://duckduckgo.com/?q={query} 52 | url: https://duckduckgo.com 53 | - name: IMDB 54 | token: i 55 | icon: simple-icons:imdb 56 | searchUrl: https://www.imdb.com/find?q={query} 57 | url: https://www.imdb.com 58 | - name: Reddit 59 | token: r 60 | icon: simple-icons:reddit 61 | searchUrl: https://www.reddit.com/search?q={query} 62 | url: https://www.reddit.com 63 | - name: YouTube 64 | token: 'y' 65 | icon: simple-icons:youtube 66 | searchUrl: https://www.youtube.com/results?search_query={query} 67 | url: https://www.youtube.com 68 | - name: Spotify 69 | token: s 70 | icon: simple-icons:spotify 71 | searchUrl: hhttps://open.spotify.com/search/{query} 72 | url: https://open.spotify.com 73 | customApps: 74 | - group: Media 75 | apps: 76 | - name: Test 77 | url: 'https://example.com' 78 | icon: 'mdi:test-tube' 79 | info: This is a test app 80 | globalBookmarks: 81 | - group: Communicate 82 | bookmarks: 83 | - name: Discord 84 | url: 'https://discord.com' 85 | - name: Gmail 86 | url: 'http://gmail.com' 87 | - name: Slack 88 | url: 'https://slack.com/signin' 89 | - group: Cloud 90 | bookmarks: 91 | - name: Box 92 | url: 'https://box.com' 93 | - name: Dropbox 94 | url: 'https://dropbox.com' 95 | - name: Drive 96 | url: 'https://drive.google.com' 97 | - group: Design 98 | bookmarks: 99 | - name: Awwwards 100 | url: 'https://awwwards.com' 101 | - name: Dribbble 102 | url: 'https://dribbble.com' 103 | - name: Muz.li 104 | url: 'https://medium.muz.li/' 105 | - group: Dev 106 | bookmarks: 107 | - name: Codepen 108 | url: 'https://codepen.io/' 109 | - name: Devdocs 110 | url: 'https://devdocs.io' 111 | - name: Devhints 112 | url: 'https://devhints.io' 113 | - group: Lifestyle 114 | bookmarks: 115 | - name: Design Milk 116 | url: 'https://design-milk.com/category/interior-design/' 117 | - name: Dwell 118 | url: 'https://www.dwell.com/' 119 | - name: Freshome 120 | url: 'https://www.mymove.com/freshome/' 121 | - group: Media 122 | bookmarks: 123 | - name: Spotify 124 | url: 'http://browse.spotify.com' 125 | - name: Trakt 126 | url: 'http://trakt.tv' 127 | - name: YouTube 128 | url: 'https://youtube.com/feed/subscriptions' 129 | - group: Reading 130 | bookmarks: 131 | - name: Instapaper 132 | url: 'https://www.instapaper.com/u' 133 | - name: Medium 134 | url: 'http://medium.com' 135 | - name: Reddit 136 | url: 'http://reddit.com' 137 | - group: Tech 138 | bookmarks: 139 | - name: TheNextWeb 140 | url: 'https://thenextweb.com/' 141 | - name: The Verge 142 | url: 'https://theverge.com/' 143 | - name: MIT Technology Review 144 | url: 'https://www.technologyreview.com/' 145 | ingress: 146 | main: 147 | enabled: true 148 | hosts: 149 | - host: hajimari.domain.tld 150 | paths: 151 | - path: / 152 | pathType: Prefix 153 | persistence: 154 | data: 155 | enabled: true 156 | accessMode: ReadWriteOnce 157 | size: 1Gi 158 | ``` 159 | 160 | {{ template "chart.sourcesSection" . }} 161 | 162 | {{ template "chart.requirementsSection" . }} 163 | 164 | {{ template "chart.valuesSection" . }} 165 | -------------------------------------------------------------------------------- /charts/hajimari/ci/ct-values.yaml: -------------------------------------------------------------------------------- 1 | hajimari: 2 | namespaceSelector: 3 | matchNames: 4 | - downloads 5 | - media 6 | customApps: 7 | - group: media 8 | apps: 9 | - name: Test 10 | url: https://example.com 11 | icon: test-tube 12 | - name: Example 13 | url: https://example.org 14 | icon: test-tube 15 | globalBookmarks: 16 | - group: Communicate 17 | bookmarks: 18 | - name: Discord 19 | url: 'https://discord.com' 20 | - name: Gmail 21 | url: 'http://gmail.com' 22 | - name: Slack 23 | url: 'https://slack.com/signin' 24 | - group: Cloud 25 | bookmarks: 26 | - name: Box 27 | url: 'https://box.com' 28 | - name: Dropbox 29 | url: 'https://dropbox.com' 30 | - name: Drive 31 | url: 'https://drive.google.com' 32 | ingress: 33 | main: 34 | enabled: true 35 | hosts: 36 | - host: hajimari.domain.tld 37 | paths: 38 | - path: / 39 | pathType: Prefix 40 | persistence: 41 | data: 42 | enabled: true -------------------------------------------------------------------------------- /charts/hajimari/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | {{- include "common.values.setup" . }} 2 | {{ include "common.notes.defaultNotes" . }} -------------------------------------------------------------------------------- /charts/hajimari/templates/app-sample.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.createCRAppSample }} 2 | 3 | apiVersion: hajimari.io/v1alpha1 4 | kind: Application 5 | metadata: 6 | name: hajimari-issues 7 | spec: 8 | name: Hajimari Issues 9 | group: info 10 | icon: simple-icons:github 11 | url: https://github.com/toboshii/hajimari/issues 12 | info: Submit issue to this project 13 | targetBlank: true 14 | 15 | {{- end }} 16 | -------------------------------------------------------------------------------- /charts/hajimari/templates/common.yaml: -------------------------------------------------------------------------------- 1 | {{- include "common.values.setup" . }} 2 | 3 | {{- define "hajimari.settingsVolume" -}} 4 | enabled: "true" 5 | mountPath: "/config/config.yaml" 6 | subPath: "config.yaml" 7 | type: "custom" 8 | volumeSpec: 9 | configMap: 10 | name: {{ include "common.names.fullname" . }}-settings 11 | {{- end -}} 12 | {{- $_ := set .Values.persistence "hajimari-settings" (include "hajimari.settingsVolume" . | fromYaml) -}} 13 | 14 | {{ include "common.all" . }} -------------------------------------------------------------------------------- /charts/hajimari/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- include "common.values.setup" . }} 2 | 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: {{ include "common.names.fullname" . }}-settings 7 | labels: {{- include "common.labels" . | nindent 4 }} 8 | data: 9 | config.yaml: |- 10 | {{ toYaml .Values.hajimari | indent 4 }} -------------------------------------------------------------------------------- /charts/hajimari/templates/crd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: applications.hajimari.io 7 | spec: 8 | conversion: 9 | strategy: None 10 | group: hajimari.io 11 | names: 12 | kind: Application 13 | listKind: ApplicationList 14 | plural: applications 15 | singular: application 16 | preserveUnknownFields: false 17 | scope: Namespaced 18 | versions: 19 | - name: v1alpha1 20 | served: true 21 | storage: true 22 | schema: 23 | openAPIV3Schema: 24 | type: object 25 | properties: 26 | apiVersion: 27 | type: string 28 | kind: 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | type: object 34 | required: 35 | - name 36 | - group 37 | - url 38 | properties: 39 | name: 40 | type: string 41 | group: 42 | type: string 43 | icon: 44 | type: string 45 | url: 46 | type: string 47 | info: 48 | type: string 49 | targetBlank: 50 | type: boolean 51 | status: 52 | type: object 53 | -------------------------------------------------------------------------------- /charts/hajimari/templates/rbac.yaml: -------------------------------------------------------------------------------- 1 | {{- include "common.values.setup" . }} 2 | {{- if .Values.serviceAccount.create }} 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | name: {{ include "common.names.fullname" . }} 7 | labels: {{- include "common.labels" . | nindent 4 }} 8 | rules: 9 | - apiGroups: ["", "extensions", "networking.k8s.io", "discovery.k8s.io"] 10 | resources: ["ingresses", "namespaces", "endpointslices"] 11 | verbs: ["get", "list"] 12 | - apiGroups: ["hajimari.io"] 13 | resources: ["applications"] 14 | verbs: ["get", "list"] 15 | --- 16 | apiVersion: rbac.authorization.k8s.io/v1 17 | kind: ClusterRoleBinding 18 | metadata: 19 | name: {{ include "common.names.fullname" . }} 20 | labels: {{- include "common.labels" . | nindent 4 }} 21 | roleRef: 22 | apiGroup: rbac.authorization.k8s.io 23 | kind: ClusterRole 24 | name: {{ include "common.names.fullname" . }} 25 | subjects: 26 | - kind: ServiceAccount 27 | name: {{ include "common.names.fullname" . }} 28 | namespace: {{ .Release.Namespace }} 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /charts/hajimari/values.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # IMPORTANT NOTE 3 | # 4 | # This chart inherits from the bjw-s library chart. You can check the default values/options here: 5 | # https://github.com/bjw-s/helm-charts/tree/main/charts/library/common 6 | # 7 | 8 | image: 9 | # -- image repository 10 | repository: ghcr.io/toboshii/hajimari 11 | # -- image pull policy 12 | pullPolicy: IfNotPresent 13 | # -- image tag 14 | tag: v0.3.1 15 | 16 | # -- environment variables. 17 | # @default -- See below 18 | env: 19 | # -- Set the container timezone 20 | TZ: UTC 21 | 22 | # -- Configures Hajimari settings for this instance. 23 | # @default -- See below 24 | hajimari: 25 | # -- The name of this instance, this allows running multiple 26 | # instances of Hajimari on the same cluster 27 | instanceName: null 28 | 29 | # -- Set to true to show all discovered applications by default. 30 | defaultEnable: false 31 | 32 | # -- Namespace selector to use for discovering applications 33 | namespaceSelector: 34 | matchNames: 35 | - media 36 | 37 | # -- Override the title of the Hajimari pages 38 | title: null 39 | 40 | # -- Default name for welcome message 41 | name: "You" 42 | 43 | # -- Add custom applications to the discovered application list 44 | customApps: [] 45 | # - group: Media 46 | # apps: 47 | # - name: Test 48 | # url: 'https://example.com' 49 | # icon: 'mdi:test-tube' 50 | # info: This is a test app 51 | 52 | # -- Create sample Custom Resource Application 53 | createCRAppSample: false 54 | 55 | # -- Set default bookmarks 56 | globalBookmarks: [] 57 | # - group: Communicate 58 | # bookmarks: 59 | # - name: Discord 60 | # url: 'https://discord.com' 61 | # - name: Gmail 62 | # url: 'http://gmail.com' 63 | # - name: Slack 64 | # url: 'https://slack.com/signin' 65 | 66 | 67 | # -- Configures service settings for the chart. 68 | # @default -- See values.yaml 69 | service: 70 | main: 71 | ports: 72 | http: 73 | port: 3000 74 | 75 | # -- Configures service account needed for reading k8s ingress objects 76 | # @default -- See below 77 | serviceAccount: 78 | # -- Create service account 79 | create: true 80 | 81 | ingress: 82 | # -- Enable and configure ingress settings for the chart under this key. 83 | # @default -- See values.yaml 84 | main: 85 | enabled: false 86 | 87 | # -- Configure persistence settings for the chart under this key. 88 | # @default -- See values.yaml 89 | persistence: 90 | data: 91 | enabled: true 92 | type: pvc 93 | accessMode: ReadWriteOnce 94 | size: 1Gi 95 | -------------------------------------------------------------------------------- /cmd/hajimari/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | 7 | "github.com/fsnotify/fsnotify" 8 | "github.com/spf13/viper" 9 | "github.com/toboshii/hajimari/internal/config" 10 | "github.com/toboshii/hajimari/internal/handlers" 11 | "github.com/toboshii/hajimari/internal/log" 12 | ) 13 | 14 | var ( 15 | logger = log.New() 16 | ) 17 | 18 | func init() { 19 | viper.SetConfigName("config") // name of config file (without extension) 20 | viper.AddConfigPath("/config") // path to look for the config file in 21 | viper.AddConfigPath("$HOME/.hajimari") // call multiple times to add many search paths 22 | viper.AddConfigPath(".") // optionally look for config in the working directory 23 | viper.AutomaticEnv() 24 | 25 | err := viper.ReadInConfig() // Find and read the config file 26 | 27 | if err != nil { // Handle errors reading the config file 28 | panic(errors.New("Fatal error config file: " + err.Error())) 29 | } 30 | 31 | logger.Info("Using config file: ", viper.ConfigFileUsed()) 32 | 33 | viper.OnConfigChange(func(e fsnotify.Event) { 34 | logger.Info("Reloading config: ", e.Name) 35 | }) 36 | 37 | viper.WatchConfig() 38 | 39 | config.SetDefaults() 40 | } 41 | 42 | func main() { 43 | 44 | // appConfig, err := config.GetConfig() 45 | // if err != nil { 46 | // logger.Fatal("Failed to read configuration for hajimari", err) 47 | // return 48 | // } 49 | 50 | httpHandler := handlers.NewHandler() 51 | 52 | logger.Printf("Listening on :%d", 3000) 53 | logger.Fatal(http.ListenAndServe(":3000", httpHandler)) 54 | } 55 | -------------------------------------------------------------------------------- /config.yaml.example: -------------------------------------------------------------------------------- 1 | instanceName: null 2 | defaultEnable: true 3 | namespaceSelector: 4 | matchNames: 5 | - default 6 | - downloads 7 | - media 8 | name: You 9 | title: Hajimari 10 | lightTheme: gazette 11 | darkTheme: horizon 12 | customThemes: 13 | - name: danger 14 | backgroundColor: '#0c0e0c' 15 | primaryColor: '#eaebea' 16 | accentColor: '#d8323c' 17 | showGreeting: true 18 | showAppGroups: false 19 | showAppUrls: true 20 | showAppInfo: false 21 | showAppStatus: true 22 | defaultAppIcon: mdi:application 23 | showBookmarkGroups: true 24 | showGlobalBookmarks: false 25 | alwaysTargetBlank: false 26 | defaultSearchProvider: Google 27 | searchProviders: 28 | - name: Google 29 | token: g 30 | icon: simple-icons:google 31 | searchUrl: https://www.google.com/search?q={query} 32 | url: https://www.google.com 33 | - name: DuckDuckGo 34 | token: d 35 | icon: simple-icons:duckduckgo 36 | searchUrl: https://duckduckgo.com/?q={query} 37 | url: https://duckduckgo.com 38 | - name: IMDB 39 | token: i 40 | icon: simple-icons:imdb 41 | searchUrl: https://www.imdb.com/find?q={query} 42 | url: https://www.imdb.com 43 | - name: Reddit 44 | token: r 45 | icon: simple-icons:reddit 46 | searchUrl: https://www.reddit.com/search?q={query} 47 | url: https://www.reddit.com 48 | - name: YouTube 49 | token: 'y' 50 | icon: simple-icons:youtube 51 | searchUrl: https://www.youtube.com/results?search_query={query} 52 | url: https://www.youtube.com 53 | - name: Spotify 54 | token: s 55 | icon: simple-icons:spotify 56 | searchUrl: hhttps://open.spotify.com/search/{query} 57 | url: https://open.spotify.com 58 | customApps: 59 | - group: Media 60 | apps: 61 | - name: Test 62 | url: 'https://example.com' 63 | icon: 'mdi:test-tube' 64 | info: This is a test app 65 | globalBookmarks: 66 | - group: Communicate 67 | bookmarks: 68 | - name: Discord 69 | url: 'https://discord.com' 70 | - name: Gmail 71 | url: 'http://gmail.com' 72 | - name: Slack 73 | url: 'https://slack.com/signin' 74 | - group: Cloud 75 | bookmarks: 76 | - name: Box 77 | url: 'https://box.com' 78 | - name: Dropbox 79 | url: 'https://dropbox.com' 80 | - name: Drive 81 | url: 'https://drive.google.com' 82 | - group: Design 83 | bookmarks: 84 | - name: Awwwards 85 | url: 'https://awwwards.com' 86 | - name: Dribbble 87 | url: 'https://dribbble.com' 88 | - name: Muz.li 89 | url: 'https://medium.muz.li/' 90 | - group: Dev 91 | bookmarks: 92 | - name: Codepen 93 | url: 'https://codepen.io/' 94 | - name: Devdocs 95 | url: 'https://devdocs.io' 96 | - name: Devhints 97 | url: 'https://devhints.io' 98 | - group: Lifestyle 99 | bookmarks: 100 | - name: Design Milk 101 | url: 'https://design-milk.com/category/interior-design/' 102 | - name: Dwell 103 | url: 'https://www.dwell.com/' 104 | - name: Freshome 105 | url: 'https://www.mymove.com/freshome/' 106 | - group: Media 107 | bookmarks: 108 | - name: Spotify 109 | url: 'http://browse.spotify.com' 110 | - name: Trakt 111 | url: 'http://trakt.tv' 112 | - name: YouTube 113 | url: 'https://youtube.com/feed/subscriptions' 114 | - group: Reading 115 | bookmarks: 116 | - name: Instapaper 117 | url: 'https://www.instapaper.com/u' 118 | - name: Medium 119 | url: 'http://medium.com' 120 | - name: Reddit 121 | url: 'http://reddit.com' 122 | - group: Tech 123 | bookmarks: 124 | - name: TheNextWeb 125 | url: 'https://thenextweb.com/' 126 | - name: The Verge 127 | url: 'https://theverge.com/' 128 | - name: MIT Technology Review 129 | url: 'https://www.technologyreview.com/' 130 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | .vercel 10 | .output 11 | .vite -------------------------------------------------------------------------------- /frontend/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Hajimari Frontend 2 | 3 | Hajimari's web UI is implemented as an SPA (single page app) utilizing the [SvelteKit](https://kit.svelte.dev/) static adapter. 4 | 5 | ## Developing 6 | 7 | Once you've checked out the project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 8 | 9 | ```bash 10 | npm run dev 11 | 12 | # or start the server and open the app in a new browser tab 13 | npm run dev -- --open 14 | ``` 15 | 16 | ## Building 17 | 18 | To create a production version of your app: 19 | 20 | ```bash 21 | npm run build 22 | ``` 23 | 24 | You can preview the production build with `npm run preview`. 25 | -------------------------------------------------------------------------------- /frontend/frontend.go: -------------------------------------------------------------------------------- 1 | package frontend 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | // Embed the build directory from the frontend. 8 | // 9 | //go:embed all:build 10 | var BuildFs embed.FS 11 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hajimari", 3 | "version": "0.3.0", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 9 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 10 | }, 11 | "devDependencies": { 12 | "@iconify/svelte": "3.1.0", 13 | "@sveltejs/adapter-static": "1.0.0-next.44", 14 | "@sveltejs/kit": "1.0.0-next.507", 15 | "@types/codemirror": "5.60.5", 16 | "@types/js-yaml": "4.0.5", 17 | "svelte": "3.50.1", 18 | "svelte-check": "2.9.1", 19 | "svelte-preprocess": "4.10.7", 20 | "tslib": "2.4.0", 21 | "typescript": "4.8.4", 22 | "vite": "3.1.7" 23 | }, 24 | "type": "module", 25 | "dependencies": { 26 | "@fontsource/roboto": "5.0.1", 27 | "codemirror": "5.65.9", 28 | "js-yaml": "4.1.0", 29 | "svelte-portal": "2.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/app.css: -------------------------------------------------------------------------------- 1 | @import '@fontsource/roboto/400.css'; 2 | @import '@fontsource/roboto/500.css'; 3 | @import '@fontsource/roboto/700.css'; 4 | @import '@fontsource/roboto/900.css'; 5 | 6 | :root{ 7 | --color-background: #232530; 8 | --color-text-pri: #FAB795; 9 | --color-text-acc: #E95678; 10 | --font: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif; 11 | --font-code: monospace; 12 | --font-mono: monospace; 13 | } 14 | 15 | html{ 16 | box-sizing: border-box; 17 | moz-box-sizing: border-box; 18 | webkit-box-sizing: border-box; 19 | webkit-text-size-adjust: none; 20 | } 21 | 22 | ::-webkit-scrollbar-thumb {background: var(--color-text-acc); border: 5px solid var(--color-background); border-radius: 10px;} 23 | ::-webkit-scrollbar-track {background: var(--color-text-acc); border: 7px solid var(--color-background);} 24 | ::-webkit-scrollbar {width: 15px;} 25 | ::-webkit-scrollbar-corner { background: var(--color-background); } 26 | 27 | html, 28 | body{ 29 | background-color: var(--color-background); 30 | color: var(--color-text-pri); 31 | scrollbar-color: var(--color-text-acc) var(--color-background); 32 | scrollbar-width: thin; 33 | font-family: var(--font); 34 | font-size: 14px; 35 | font-weight: 400; 36 | height: auto; 37 | letter-spacing: -.012em; 38 | margin: 0; 39 | padding: 0; 40 | webkit-font-smoothing: antialiased; 41 | width: 100vw; 42 | } 43 | 44 | *, 45 | *:before, 46 | *:after{ 47 | box-sizing: inherit; 48 | moz-box-sizing: inherit; 49 | webkit-box-sizing: inherit; 50 | } 51 | 52 | /* TEXT STYLES */ 53 | 54 | h1, h2{ 55 | font-weight: 300; 56 | margin: 0; 57 | padding: 0; 58 | text-align: left; 59 | } 60 | 61 | h2, h3, h4{ 62 | text-transform: uppercase; 63 | } 64 | 65 | h1{ 66 | font-size: 4em; 67 | font-weight: 700; 68 | margin-bottom: 0.5em; 69 | } 70 | 71 | h2{ 72 | font-size: 16px; 73 | height: 30px; 74 | 75 | } 76 | 77 | h3{ 78 | font-size: 20px; 79 | font-weight: 900; 80 | height: 10px; 81 | } 82 | 83 | h4{ 84 | font-size: 1.1em; 85 | font-weight: 400; 86 | height: 10px; 87 | } 88 | 89 | a{ 90 | color: var(--color-text-pri); 91 | text-decoration: none; 92 | } 93 | 94 | a:hover{ 95 | text-decoration: underline; 96 | webkit-text-decoration-color: var(--color-text-acc); 97 | webkit-text-decoration-skip: true; 98 | } 99 | 100 | /* FORMS */ 101 | 102 | input{ 103 | background-color: transparent; 104 | border: 0; 105 | border-bottom: thin solid var(--color-text-acc); 106 | color: var(--color-text-pri); 107 | font-size: 0.8em; 108 | height: 3.5em; 109 | transition: all 0.4s ease; 110 | width: 100%; 111 | } 112 | 113 | input:focus{ 114 | color-border: var(--color-text-pri); 115 | outline: none; 116 | } 117 | 118 | input:focus{ 119 | opacity: 1; 120 | } 121 | 122 | 123 | /* TABLES */ 124 | 125 | table{ 126 | border: thin solid var(--color-text-acc); 127 | border-collapse: collapse; 128 | border-spacing: 0; 129 | font-size: 1em; 130 | text-align: left; 131 | width: 100%; 132 | } 133 | 134 | table td:nth-of-type(2){ 135 | padding-right: 5em; 136 | } 137 | 138 | table td{ 139 | border: thin solid var(--color-text-acc); 140 | color: var(--color-text-pri); 141 | font-size: 1em; 142 | overflow: hidden; 143 | padding: 10px 5px; 144 | word-break: normal; 145 | } 146 | 147 | table th{ 148 | border: thin solid var(--color-text-acc); 149 | color: var(--color-text-pri); 150 | font-weight: bold; 151 | padding: 10px 5px; 152 | } 153 | 154 | table a{ 155 | color: var(--color-text-pri); 156 | } 157 | 158 | /* MEDIA QUERIES */ 159 | 160 | @media screen and (max-width: 667px) { 161 | html { 162 | font-size: calc(16px + 6 * ((100vw - 320px) / 680)); 163 | } 164 | 165 | h1 { 166 | font-size: 4em; 167 | height: auto; 168 | margin-bottom: 0em; 169 | } 170 | 171 | h2 { 172 | font-size: 1em; 173 | height: auto; 174 | margin-bottom: 0em; 175 | } 176 | 177 | h3 { 178 | font-size: 1em; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /frontend/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | // and what to do when importing types 4 | declare namespace App { 5 | // interface Locals {} 6 | 7 | // interface PageData {} 8 | 9 | // interface Platform {} 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 24 | %sveltekit.head% 25 | 26 | 27 | 28 |
%sveltekit.body%
29 | 30 | 31 | -------------------------------------------------------------------------------- /frontend/src/lib/AppList/AppGroup.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#each group.apps || [] as app (app.name)} 15 |
20 | 40 |
41 | {app.name} 42 | {#if showUrl} 43 | {app.url} 44 | {/if} 45 | {#if showInfo} 46 | {app.info} 47 | {/if} 48 |
49 |
50 | {/each} 51 | 52 | 134 | -------------------------------------------------------------------------------- /frontend/src/lib/AppList/index.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 |

Applications

16 | {#if apps.length === 0} 17 |

No apps here...yet

18 | {:else} 19 |
20 | {#each apps as group} 21 | {#if showGroups} 22 | 35 | {:else} 36 | 44 | {/if} 45 | {/each} 46 |
47 | {/if} 48 |
49 | 50 | 98 | -------------------------------------------------------------------------------- /frontend/src/lib/BookmarkList/BookmarkGroup.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | 27 | 46 | -------------------------------------------------------------------------------- /frontend/src/lib/BookmarkList/index.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if bookmarks.length === 0} 11 | 15 | {:else} 16 | 31 | {/if} 32 | 33 | 70 | -------------------------------------------------------------------------------- /frontend/src/lib/CodeMirror/CodeMirror.svelte: -------------------------------------------------------------------------------- 1 | 169 | 170 | 171 | 173 | 174 | 175 |
176 |