{"id":37203946,"url":"https://github.com/asendia/legacy-api","last_synced_at":"2026-01-14T23:30:58.152Z","repository":{"id":43355741,"uuid":"456000188","full_name":"asendia/legacy-api","owner":"asendia","description":"Backend code of sejiwo.com","archived":false,"fork":false,"pushed_at":"2025-09-28T09:48:22.000Z","size":160,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-28T11:41:11.179Z","etag":null,"topics":["golang","testament","wiil"],"latest_commit_sha":null,"homepage":"https://sejiwo.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asendia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-02-05T22:25:28.000Z","updated_at":"2025-09-28T09:48:25.000Z","dependencies_parsed_at":"2024-06-20T20:44:24.964Z","dependency_job_id":"ab0b056d-c9df-4417-bea7-fb57edf69b57","html_url":"https://github.com/asendia/legacy-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/asendia/legacy-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asendia%2Flegacy-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asendia%2Flegacy-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asendia%2Flegacy-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asendia%2Flegacy-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asendia","download_url":"https://codeload.github.com/asendia/legacy-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asendia%2Flegacy-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28438422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T22:37:52.437Z","status":"ssl_error","status_checked_at":"2026-01-14T22:37:31.496Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","testament","wiil"],"created_at":"2026-01-14T23:30:57.088Z","updated_at":"2026-01-14T23:30:58.135Z","avatar_url":"https://github.com/asendia.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# legacy-api\nBackend API code for [sejiwo.com](https://sejiwo.com/)\n\n## How Sejiwo Works\n\nSejiwo is an automated digital will service that delivers your final message to loved ones only if you become unresponsive.\n\n```mermaid\nflowchart LR\n    A[User Creates Will] --\u003e B[Set Recipients]\n    B --\u003e C[Configure Timing]\n    C --\u003e D[System Activated]\n    \n    D --\u003e E{Periodic Check}\n    E --\u003e|User Responds| F[Timer Reset]\n    E --\u003e|No Response| G[Auto Delivery]\n    \n    F --\u003e E\n    G --\u003e H[Message Delivered]\n    \n    style A fill:#2563eb,stroke:#1e40af,stroke-width:2px,color:#ffffff\n    style B fill:#6366f1,stroke:#4f46e5,stroke-width:2px,color:#ffffff\n    style C fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#ffffff\n    style D fill:#06b6d4,stroke:#0891b2,stroke-width:2px,color:#ffffff\n    style E fill:#64748b,stroke:#475569,stroke-width:2px,color:#ffffff\n    style F fill:#10b981,stroke:#059669,stroke-width:2px,color:#ffffff\n    style G fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#ffffff\n    style H fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#ffffff\n    \n    classDef default font-size:14px,font-weight:500\n```\n\n### Key Features:\n- ⏰ **Automatic Delivery**: Messages delivered only when you don't respond to reminders\n- 🔒 **Secure**: AES encrypted message storage\n- 📧 **Flexible Recipients**: Send to up to 3 people\n- 🔄 **Stay in Control**: Easy to postpone or cancel anytime\n- ⚡ **Set and Forget**: Fully automated once configured\n\n📋 **[View Technical Architecture \u0026 System Details](#technical-architecture)**\n\n## Prerequisites\n- [Go 1.24](https://go.dev/doc/install)\n- [Postgresql 17.6](https://www.postgresql.org/download/)\n- [sqlc](https://docs.sqlc.dev/en/latest/overview/install.html) (Optional, for generating db structs from data/schema.sql \u0026 data/query.sql)\n- [pgAdmin4](https://www.pgadmin.org/download/) (Optional, to manage the database or use psql instead)\n- [gcloud cli](https://cloud.google.com/sdk/docs/install) (Optional, for deploying the api to Google Cloud Platform)\n\n## Development\n### Database setup\nAfter installing go \u0026 postgresql\n```sh\n./init-db.sh # Prepare dev database - set proper passwords \u0026 secrets for production\n```\n\n### Testing\nThis is integration test, you will need to run the database first before running the test\n```sh\ncp .env-test-template.yaml .env-test.yaml\ngo test ./...\n```\nWhy do I use template config? Because I put secrets in my `.env-test.yaml` \u0026 I don't want to accidentally commit it. Please let me know how to do it better.\n\n### Running the app in localhost\nFrom the root directory of this repo\n```sh\n# You need to specify the env because the default value is \"test\"\n# and I use the env to customize static file directories\nENVIRONMENT=dev go run cmd/main.go # Or just use vscode debug feature\n```\n\n### API call examples\n1. Install [thunder client](https://www.thunderclient.com/), a vscode extension similar to postman\n2. Import `thunder-collection_legacy-api.json` from thunder client\n\n## Deployment\n1. Create the secrets needed to run the apps\n```sh\necho -n \"PUT_THE_DB_PASSWORD_HERE\" | \\\n  gcloud secrets create \"db_password\" --replication-policy \"automatic\" --data-file -\n\n# This one needs to be exactly 69 characters length\necho -n \"PUT_THE_STATIC_SECRET_HERE\" | \\\n  gcloud secrets create \"static_secret\" --replication-policy \"automatic\" --data-file -\n\n# 32 characters length for AES encryption\necho -n \"PUT_THE_ENCRYPTION_KEY_HERE\" | \\\n  gcloud secrets create \"encryption_key\" --replication-policy \"automatic\" --data-file -\n\n# Additional SSL cert for Supabase\n# Download from https://supabase.com/docs/guides/database/connecting-to-postgres#connecting-with-ssl\ncat prod-ca-2021.crt | \\\n  gcloud secrets create \"supabase_ssl_certificate\" --replication-policy \"automatic\" --data-file -\n\n# To send emails\necho -n \"PUT_THE_MAILJET_API_KEY_HERE\" | \\\n  gcloud secrets create \"mailjet_api_key\" --replication-policy \"automatic\" --data-file -\n\n# To send emails\necho -n \"PUT_THE_MAILJET_SECRET_KEY_HERE\" | \\\n  gcloud secrets create \"mailjet_secret_key\" --replication-policy \"automatic\" --data-file -\n```\n2. Give the secret manager read access to your project service account.\n```sh\ngcloud projects add-iam-policy-binding [YOUR_GCLOUD_PROJECT_NAME] --member='serviceAccount:[YOUR_GCLOUD_PROJECT_NAME]@appspot.gserviceaccount.com' --role='roles/secretmanager.secretAccessor'\n```\n3. Prepare the DB\nConnect to supabase: https://supabase.com/docs/guides/database/connecting-to-postgres#direct-connections\nThen using psql or pgAdmin:\n```sh\n##################################################################\n# Copy paste the query in data/seed.sql, edit the PASSWORD field #\n##################################################################\n\n# Switch to project_legacy database\n\\c project_legacy\n\n###########################################\n# Copy paste the query in data/schema.sql #\n###########################################\n```\n4. Deploy the Cloud Run service\n```sh\n# Copy env\ncp .env-prod-template.yaml .env-prod.yaml\n# Then edit the .env-prod.yaml, follow the comments provided in the file\n\ngcloud run deploy legacy-api --source . \\\n  --region=asia-southeast1 --allow-unauthenticated --timeout 15s \\\n  --min-instances 0 --max-instances 100 --cpu 1 --memory 128Mi \\\n  --set-secrets DB_PASSWORD=db_password:latest,STATIC_SECRET=static_secret:latest,ENCRYPTION_KEY=encryption_key:latest,MAILJET_API_KEY=mailjet_api_key:latest,MAILJET_SECRET_KEY=mailjet_secret_key:latest \\\n  --env-vars-file .env-prod.yaml --update-labels service=legacy --tag=main\n```\n5. Deploy the scheduler\n```sh\n# Create a pub/sub topic - this might take a while\ngcloud pubsub topics create project-legacy-scheduler\n\n# Create a google cloud scheduler\ngcloud scheduler jobs create pubsub SendReminderMessages --location asia-southeast1 --schedule \"22 19 * * *\" \\\n  --topic project-legacy-scheduler --attributes action=send-reminder-messages \\\n  --description \"Send reminder messages daily\" --time-zone \"Asia/Jakarta\"\ngcloud scheduler jobs create pubsub SendTestaments --location asia-southeast1 --schedule \"38 19 * * *\" \\\n  --topic project-legacy-scheduler --attributes action=send-testaments \\\n  --description \"Send reminder messages daily\" --time-zone \"Asia/Jakarta\"\n\n# Copy env\ncp .env.prod-cloud-function-template.yaml .env-prod-cloud-function.yaml\n\n# CloudFunctionForSchedulerWithStaticSecret: legacy-api-scheduler\ngcloud functions deploy legacy-api-scheduler \\\n  --entry-point CloudFunctionForSchedulerWithStaticSecret --trigger-topic project-legacy-scheduler \\\n  --region asia-southeast1 --runtime go124 --memory 128Mi --timeout 15s --gen2 \\\n  --update-labels service=legacy --max-instances 10 \\\n  --set-secrets DB_PASSWORD=db_password:latest,STATIC_SECRET=static_secret:latest,ENCRYPTION_KEY=encryption_key:latest,MAILJET_API_KEY=mailjet_api_key:latest,MAILJET_SECRET_KEY=mailjet_secret_key:latest \\\n  --env-vars-file .env-prod-cloud-function.yaml\n```\n\n---\n\n## Technical Architecture\n\n### System Architecture\n\n```mermaid\ngraph TB\n    subgraph CLIENT [\"📱 Client\"]\n        WEB[Frontend\u003cbr/\u003esejiwo.com]\n    end\n    \n    subgraph GATEWAY [\"🌐 API Gateway\"]\n        LB[Load Balancer]\n        MAIN[HTTP Server\u003cbr/\u003e:8080]\n    end\n    \n    subgraph API [\"🔌 Endpoints\"]\n        API1[\"/legacy-api\u003cbr/\u003eJWT Auth\"]\n        API2[\"/legacy-api-secret\u003cbr/\u003eUser Secret\"]\n        API3[\"/legacy-api-scheduler\u003cbr/\u003eStatic Secret\"]\n    end\n    \n    subgraph LOGIC [\"⚡ Business Logic\"]\n        FRONTEND[Frontend APIs]\n        SCHEDULER[Scheduler APIs]\n    end\n    \n    subgraph DATA [\"💾 Data Layer\"]\n        DB[(PostgreSQL\u003cbr/\u003eDatabase)]\n        CACHE[Connection\u003cbr/\u003ePool]\n    end\n    \n    subgraph EXTERNAL [\"🔗 External Services\"]\n        MAILJET[Email\u003cbr/\u003eService]\n        SECRETS[Secret\u003cbr/\u003eManager]\n        PUBSUB[Message\u003cbr/\u003eQueue]\n    end\n    \n    subgraph SECURITY [\"🔒 Security\"]\n        ENC[AES\u003cbr/\u003eEncryption]\n        JWT[JWT\u003cbr/\u003eVerifier]\n        SEC[Secret\u003cbr/\u003eGenerator]\n    end\n    \n    subgraph CRON [\"⏰ Automation\"]\n        CRON1[Daily Reminders\u003cbr/\u003e19:22]\n        CRON2[Send Testaments\u003cbr/\u003e19:38]\n    end\n    \n    %% Primary Flow\n    WEB --\u003e LB\n    LB --\u003e MAIN\n    MAIN --\u003e API1 \u0026 API2 \u0026 API3\n    \n    API1 \u0026 API2 --\u003e FRONTEND\n    API3 --\u003e SCHEDULER\n    \n    FRONTEND \u0026 SCHEDULER --\u003e DB\n    FRONTEND \u0026 SCHEDULER --\u003e ENC\n    \n    %% External Connections\n    DB -.-\u003e CACHE\n    FRONTEND \u0026 SCHEDULER --\u003e MAILJET\n    ENC \u0026 MAILJET \u0026 DB --\u003e SECRETS\n    \n    %% Authentication\n    API1 --\u003e JWT\n    JWT -.-\u003e WEB\n    \n    %% Scheduling\n    CRON1 \u0026 CRON2 --\u003e PUBSUB\n    PUBSUB --\u003e API3\n    \n    %% Modern Styling\n    style CLIENT fill:#1e293b,stroke:#334155,stroke-width:2px,color:#f1f5f9\n    style GATEWAY fill:#0f172a,stroke:#334155,stroke-width:2px,color:#f1f5f9\n    style API fill:#164e63,stroke:#0891b2,stroke-width:2px,color:#f0f9ff\n    style LOGIC fill:#3730a3,stroke:#4f46e5,stroke-width:2px,color:#f0f9ff\n    style DATA fill:#7c2d12,stroke:#ea580c,stroke-width:2px,color:#fef7ed\n    style EXTERNAL fill:#166534,stroke:#16a34a,stroke-width:2px,color:#f0fdf4\n    style SECURITY fill:#991b1b,stroke:#dc2626,stroke-width:2px,color:#fef2f2\n    style CRON fill:#6b21a8,stroke:#9333ea,stroke-width:2px,color:#faf5ff\n```\n\n### Database Schema\n\n```mermaid\nerDiagram\n    EMAILS {\n        varchar email PK \"Primary identifier\"\n        timestamp created_at \"Registration time\"\n        boolean is_active \"Account status\"\n    }\n    \n    MESSAGES {\n        uuid id PK \"Message identifier\"\n        varchar email_creator FK \"Message author\"\n        timestamp created_at \"Creation time\"\n        varchar content_encrypted \"Encrypted content\"\n        integer inactive_period_days \"Delivery delay\"\n        integer reminder_interval_days \"Reminder frequency\"\n        boolean is_active \"Message status\"\n        char extension_secret \"Extension token\"\n        date inactive_at \"Delivery date\"\n        date next_reminder_at \"Next reminder\"\n        integer sent_counter \"Delivery attempts\"\n    }\n    \n    RECEIVERS {\n        uuid message_id FK \"Message reference\"\n        varchar email_receiver FK \"Recipient email\"\n        boolean is_unsubscribed \"Subscription status\"\n        char unsubscribe_secret \"Unsubscribe token\"\n    }\n    \n    EMAILS ||--o{ MESSAGES : creates\n    EMAILS ||--o{ RECEIVERS : receives\n    MESSAGES ||--o{ RECEIVERS : \"sent to\"\n```\n\n### Key Technical Features:\n- **🏗️ Architecture**: Go HTTP server on Google Cloud Run\n- **🔐 Security**: AES encryption, JWT authentication, secret management\n- **📊 Database**: PostgreSQL with optimized indexes for queries\n- **📧 Email**: Mailjet integration with HTML templates\n- **⏰ Scheduling**: Google Cloud Scheduler + Pub/Sub\n- **🔄 Scalability**: Stateless design, connection pooling\n- **📈 Monitoring**: Structured logging and error handling\n- **🛡️ Reliability**: Transaction-based operations, retry logic\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasendia%2Flegacy-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasendia%2Flegacy-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasendia%2Flegacy-api/lists"}