Porównaj commity

...

1478 Commity
v0.11.5 ... dev

Autor SHA1 Wiadomość Data
daniel ab383ba55b
Merge pull request #5080 from pixelfed/staging
Staging
2024-05-07 04:23:54 -06:00
Daniel Supernault 29e472d6ca
Update changelog 2024-05-07 04:23:19 -06:00
Daniel Supernault 9a5e3471d4
Update AdminCuratedRegisterController, increase message length from 1000 to 3000 2024-05-07 04:22:28 -06:00
Daniel Supernault 61d105fd25
Update DirectMessageController, add 72 hour delay for new accounts before they can send a DM 2024-05-07 04:21:20 -06:00
daniel 101e620bf5
Merge pull request #5078 from pixelfed/staging
Bump version to v0.12.1
2024-05-07 01:15:50 -06:00
Daniel Supernault 2d3f1df003
Bump version to v0.12.1 2024-05-07 01:11:26 -06:00
Daniel Supernault 7c19baf5dc
Update changelog 2024-05-07 01:11:09 -06:00
Daniel Supernault 326bb93b8b
Update composer deps 2024-05-07 01:08:49 -06:00
daniel 64631c7233
Merge pull request #5077 from pixelfed/staging
Staging
2024-05-07 01:06:12 -06:00
Daniel Supernault 902572ed51
Update changelog 2024-05-07 01:01:17 -06:00
Daniel Supernault 60a62b59c9
Update ConfigCacheService, fix database race condition and fallback to file config and enable by default 2024-05-07 01:00:32 -06:00
daniel 3e59dd2868
Merge pull request #5067 from pixelfed/staging
Update ApiV1Dot1Controller, fix in app registration bug that prevents…
2024-04-30 23:29:37 -06:00
Daniel Supernault cbf996c9b6
Update ApiV1Dot1Controller, fix in app registration bug that prevents proper auth flow due to missing oauth scopes 2024-04-30 23:05:38 -06:00
daniel cf10fca74f
Merge pull request #5061 from pixelfed/staging
Bump version to v0.12.0
2024-04-29 03:12:32 -06:00
Daniel Supernault aa94dde376
Bump version to v0.12.0 2024-04-29 03:03:40 -06:00
daniel eecc7bef61
Merge pull request #5060 from pixelfed/staging
Update Like model, increase max likes per day from 500 to 1500
2024-04-29 02:47:45 -06:00
Daniel Supernault 3c877523b1
Update changelog 2024-04-29 02:47:17 -06:00
Daniel Supernault 4223119f58
Update Like model, increase max likes per day from 500 to 1500 2024-04-29 02:46:14 -06:00
daniel 74a3d5c6c0
Merge pull request #5056 from pixelfed/staging
Update CustomEmojiService, only return local emoji
2024-04-23 05:55:10 -06:00
Daniel Supernault 7f8bba4415
Update CustomEmojiService, only return local emoji 2024-04-23 05:54:24 -06:00
daniel 6263e90a13
Merge pull request #5054 from pixelfed/staging
Disable config cache by default
2024-04-21 14:10:21 -06:00
Daniel Supernault e46bd6cc06
Disable config cache by default 2024-04-21 14:09:48 -06:00
daniel ae60c99679
Merge pull request #5052 from pixelfed/staging
Update profile embed, fix height bug
2024-04-20 05:59:25 -06:00
Daniel Supernault a54b4fb038
Update profile embed, fix height bug 2024-04-20 05:59:01 -06:00
daniel 2f7481205c
Merge pull request #5051 from pixelfed/staging
Staging
2024-04-20 05:03:29 -06:00
Daniel Supernault cde17f5af7
Update changelog 2024-04-20 05:03:10 -06:00
Daniel Supernault 433bc4c286
Update embed.js 2024-04-20 05:02:58 -06:00
daniel 5d407ededf
Merge pull request #5050 from pixelfed/staging
Update profile embed view, fix height bug
2024-04-20 05:01:50 -06:00
Daniel Supernault 65166570c5
Update profile embed view, fix height bug 2024-04-20 05:01:04 -06:00
daniel 610326e7b0
Merge pull request #5049 from pixelfed/staging
Refactor embeds
2024-04-20 04:36:08 -06:00
Daniel Supernault 6fc066a213
Update changelog 2024-04-20 04:35:10 -06:00
Daniel Supernault 8b8b1ffc5c
Update ProfileController, refactor profile embeds 2024-04-20 04:33:47 -06:00
Daniel Supernault 9a7acc12a6
Update StatusController, refactor status embeds 2024-04-20 04:26:47 -06:00
Daniel Supernault 51b6fe7dc8
Refactor embeds 2024-04-20 04:25:22 -06:00
daniel c4ffa62242
Merge pull request #5048 from pixelfed/staging
Update webpack config
2024-04-20 02:01:19 -06:00
Daniel Supernault 87ee0633fe
Update assets, move presenters 2024-04-20 01:26:51 -06:00
Daniel Supernault ded660b2c4
Update webpack config 2024-04-20 00:55:21 -06:00
daniel 4d04227d41
Merge pull request #5043 from pixelfed/staging
Staging
2024-04-12 04:44:12 -06:00
Daniel Supernault 26f92c93ce
Update compiled assets 2024-04-12 04:43:38 -06:00
Daniel Supernault 81566987e4
Update changelog 2024-04-12 04:43:21 -06:00
Daniel Supernault 8af2360779
Update VideoPlayer component, add playsinline attribute to video element 2024-04-12 04:42:57 -06:00
daniel b3fb69c5b1
Merge pull request #5042 from pixelfed/staging
Staging
2024-04-12 04:03:20 -06:00
Daniel Supernault f30f7d79fb
Update changelog 2024-04-12 04:00:46 -06:00
Daniel Supernault 2deb65d874
Update compiled assets 2024-04-12 04:00:25 -06:00
Daniel Supernault ad03291699
Update VideoPresenter component, add webkit-playsinline attribute to video element to prevent the full screen video player 2024-04-12 03:56:33 -06:00
daniel 6be21891d5
Merge pull request #5041 from pixelfed/staging
Staging
2024-04-11 20:44:15 -06:00
daniel 141f6d38a7
Merge pull request #5035 from jippi/jippi-fork
Docker fixes
2024-04-11 20:43:52 -06:00
Daniel Supernault 4608c66c0b
Re-add .env.example 2024-04-11 20:43:24 -06:00
Christian Winther e227ee1bd5 use DOCKER_DB_HOST_PORT when checking if database is ready or not 2024-04-06 10:53:48 +00:00
Christian Winther dbc5df849f use ENABLE_CONFIG_CACHE when dumping composer autoload 2024-04-06 10:06:07 +00:00
Daniel Supernault 6bdf73de4d
Update UnfollowPipeline, fix follower count cache bug 2024-04-06 03:29:17 -06:00
daniel 5b3d0206ae
Merge pull request #5034 from pixelfed/staging
Update docker env, fix config_cache. Fixes #5033
2024-04-06 03:20:31 -06:00
Daniel Supernault 858fcbf606
Update docker env, fix config_cache. Fixes #5033 2024-04-06 03:19:50 -06:00
daniel 57df06a0b6
Merge pull request #5032 from pixelfed/staging
Staging
2024-04-06 02:56:48 -06:00
Daniel Supernault db1a4c9f8e
Update changelog 2024-04-06 02:48:31 -06:00
Daniel Supernault ce4beab9c8
Update composer deps 2024-04-06 02:48:03 -06:00
Daniel Supernault 9d5479de39
Update compiled assets 2024-04-06 02:47:39 -06:00
Daniel Supernault 512518d319
Update npm deps 2024-04-06 02:47:19 -06:00
Daniel Supernault b06a3455c2
Update compiled assets 2024-04-06 02:28:27 -06:00
Daniel Supernault 94a6e8614a
Update styles 2024-04-06 02:27:55 -06:00
Daniel Supernault 81d1e0fdab
Update context menu, add mute/block/unfollow actions and update relationship store accordingly 2024-04-06 02:27:22 -06:00
Daniel Supernault b8e96a5ff3
Update ApiV1Controller, improve refresh relations logic when (un)muting or (un)blocking 2024-04-06 01:24:09 -06:00
Daniel Supernault b7322b6874
Update PrivacySettings controller, refresh RelationshipService when unmute/unblocking 2024-04-06 01:22:52 -06:00
daniel 142db2c41e
Merge pull request #5031 from pixelfed/staging
Staging
2024-04-06 00:49:02 -06:00
Daniel Supernault 8c6936409d
Update relationships view, fix unfollow hashtag bug. Fixes #5008 2024-04-05 22:38:13 -06:00
daniel e2c2952fda
Merge pull request #5011 from ThisIsMissEm/feat/add-api-cors
Adjust CORS configuration to support API & OAuth Routes
2024-04-05 22:11:20 -06:00
daniel dfab7e945a
Merge pull request #5002 from jippi/docker-check-requirements
Docker: Script to check requirements
2024-04-05 22:09:59 -06:00
daniel 363196883d
Merge pull request #4985 from jippi/allow-setting-db-root-password
Docker: Allow setting the DB root password separately
2024-04-05 22:08:18 -06:00
daniel f1eaaa80be
Merge pull request #4984 from jippi/fix-rsync-arm-image
Docker: Use rsync container image that supports arm64 and amd64
2024-04-05 22:07:02 -06:00
daniel 0b162dc15e
Merge pull request #5005 from pixelfed/staging
Update Admin Settings
2024-04-05 22:05:09 -06:00
Daniel Supernault a9e54aa540
Add BeagleService 2024-04-05 22:02:51 -06:00
Daniel Supernault 3871a80391
Update compiled assets 2024-04-05 22:01:17 -06:00
Daniel Supernault 4147f7c521
Update spa sass, fix timestamp dark mode bug 2024-04-05 22:00:16 -06:00
Daniel Supernault 039dfaa6c3
Update circleci config 2024-03-31 16:10:28 -06:00
Daniel Supernault f318bd7a30
Update circleci config 2024-03-31 16:05:45 -06:00
Daniel Supernault d946afcc5c
Update AdminSettings, use better validation for user integer settings 2024-03-18 06:13:27 -06:00
Daniel Supernault 2dcbc1d5ef
Update AdminSettings, add max_account_size support 2024-03-18 06:09:36 -06:00
Daniel Supernault ec2fdd61f7
Update AdminInstances component 2024-03-18 05:58:06 -06:00
Daniel Supernault aba1e13d43
Update AdminSettings component, fix user settings 2024-03-18 05:57:21 -06:00
Daniel Supernault dcc5f416ef
Update AdminSettingsController, add AdminSettingsService 2024-03-18 05:55:38 -06:00
Emelia Smith 1eadff9d2e
Adjust CORS configuration to support API & OAuth Routes
Fixes #4411 and #3381
2024-03-17 21:43:26 +01:00
Daniel Supernault ac1f074889
Update AdminSettingsController, add user filter max limit settings 2024-03-16 05:13:06 -06:00
Daniel Supernault 5162c0704a
Update RemoteFollowImportRecent, use MediaPathService 2024-03-16 03:58:24 -06:00
Daniel Supernault 3628b4625c
Update ConfigCacheService, encrypt keys at rest 2024-03-14 05:49:02 -06:00
Daniel Supernault 674e560f04
Update admin settings, refactor to vue component 2024-03-14 05:17:18 -06:00
Daniel Supernault eb4871237b
Add admin settings partials 2024-03-14 05:07:49 -06:00
Daniel Supernault 704e7b12e0
Update AdminReadMore component, add .prevent to click action 2024-03-14 05:06:28 -06:00
Daniel Supernault cee979eda8
Update hashtag component 2024-03-14 05:05:27 -06:00
Daniel Supernault 828a456f36
Update web-admin routes, add setting api routies ;) 2024-03-14 05:04:14 -06:00
Daniel Supernault 087b27916f
Update filesystems config, add to config_cache 2024-03-14 05:03:19 -06:00
Daniel Supernault 6ce513f8c3
Update user_filters, use config_cache 2024-03-14 00:06:33 -06:00
Daniel Supernault 949e99798e
Update UserObserver, fix type casting 2024-03-13 23:36:26 -06:00
Daniel Supernault 911446c03e
Update app.name config, use config_cache 2024-03-12 06:42:12 -06:00
Daniel Supernault a76cb5f4f8
Update autospam config, use config_cache 2024-03-12 06:20:26 -06:00
Daniel Supernault da0e0ffabf
Update ig import, use config_cache 2024-03-12 06:11:20 -06:00
Daniel Supernault 2d113de536
Update config_cache, fix type casting 2024-03-12 04:27:24 -06:00
Daniel Supernault d1adb109de
Update stories config, use config_cache 2024-03-12 04:15:05 -06:00
Daniel Supernault ce228f7fa4
Update oauth setting, use config_cache 2024-03-12 03:55:51 -06:00
Daniel Supernault 5071aaf408
Update activitpub setting, use config_cache() 2024-03-12 02:20:37 -06:00
Daniel Supernault 40478f258a
Update landing settings, use config_cache 2024-03-12 02:05:22 -06:00
Daniel Supernault d670de175e
Update media_types, use config_cache 2024-03-12 01:28:08 -06:00
Daniel Supernault fecbe1897b
Update pixelfed.max_album_length, use config_cache 2024-03-12 01:20:24 -06:00
Daniel Supernault 665581d80c
Update cloud storage, use config_cache 2024-03-12 01:03:33 -06:00
Daniel Supernault a72188a7db
Update image pipeline, use config_cache 2024-03-12 00:35:33 -06:00
Daniel Supernault ad506e901d
Update AdminDirectoryController, fix type casting 2024-03-12 00:03:10 -06:00
Daniel Supernault f2f2a8097c
Update PixelfedDirectoryController, use cached stats 2024-03-12 00:02:15 -06:00
Daniel Supernault f08aab2231
Update PixelfedDirectoryController, fix boolean cast bug 2024-03-11 23:43:23 -06:00
Daniel Supernault 8a0c456edc
Update admin css, use font-display:swap for nucleo icons 2024-03-11 23:27:43 -06:00
Daniel Supernault 75081e609a
Update ProfileController, handle permalink redirect bug 2024-03-11 23:26:30 -06:00
Daniel Supernault 481314cd23
Update custom emoji, add config_cache support 2024-03-11 22:42:26 -06:00
Daniel Supernault 8a89e3c963
Update captcha, use config_cache helper 2024-03-11 21:25:04 -06:00
Daniel Supernault c96167f2f7
Update config_cache 2024-03-11 00:29:55 -06:00
Christian Winther ad382f8f55 docker: cleanup script 2024-03-10 15:16:25 +00:00
Christian Winther 3a1f4789e6 docker: I => Pixelfed 2024-03-10 15:15:08 +00:00
Christian Winther 4942f7fbd4 docker: docs 2024-03-10 15:13:45 +00:00
Christian Winther cc8c5ccd37 docker: include convenience script reference 2024-03-10 15:11:54 +00:00
Christian Winther 56d47dd1bc hide jippi-fork 2024-03-10 15:00:31 +00:00
Christian Winther 1892f68ebd Add helper script to check minimum requirements 2024-03-10 14:53:57 +00:00
Christian Winther ca7c2d34f2 Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-03-10 13:44:16 +00:00
daniel 8aae92d75b
Merge pull request #5001 from pixelfed/staging
Staging
2024-03-10 05:44:24 -06:00
Daniel Supernault bf46f6f5f4
Update config_cache 2024-03-10 05:42:25 -06:00
Daniel Supernault b0cb4456a9
Update ApiV1Dot1Controller, use config_cache for in-app registration 2024-03-10 05:06:52 -06:00
Daniel Supernault 7785a2dae4
Update Config, use config_cache 2024-03-10 04:37:22 -06:00
daniel 57f4457637
Merge pull request #5000 from pixelfed/staging
Update config cache
2024-03-10 04:20:28 -06:00
Daniel Supernault 5e4d4eff9d
Update config cache 2024-03-10 04:19:44 -06:00
daniel 3132523798
Merge pull request #4999 from pixelfed/staging
Update web-api popular accounts route to its own method to remove the…
2024-03-09 23:27:14 -07:00
Daniel Supernault a4bc5ce3d0
Update web-api popular accounts route to its own method to remove the breaking oauth scope bug 2024-03-09 23:25:28 -07:00
daniel f4086d4381
Merge pull request #4997 from pixelfed/staging
Staging
2024-03-08 06:49:17 -07:00
Daniel Supernault 37a82cfb90
Update changelog 2024-03-08 06:44:29 -07:00
Daniel Supernault 4aa0e25f4c
Update commands, add user account delete cli command to federate account deletion 2024-03-08 06:44:02 -07:00
daniel 24c467c558
Merge pull request #4996 from pixelfed/staging
Staging
2024-03-08 06:04:27 -07:00
Daniel Supernault bcce1df6fc
Update AP transformers, add DeleteActor activity 2024-03-08 06:02:11 -07:00
Daniel Supernault a969ca502f
Add migrations 2024-03-08 06:01:35 -07:00
Daniel Supernault 36c518fe2c
Update web routes 2024-03-08 05:04:27 -07:00
Daniel Supernault 95199843e3
Update SiteController, add curatedOnboarding method that gracefully falls back to open registration when applicable 2024-03-08 05:03:29 -07:00
Daniel Supernault 853a729f76
Update ProfileController, preserve deleted actor objects for federated account deletion and use more efficient account cache lookup 2024-03-08 05:00:56 -07:00
Daniel Supernault e742d595a6
Update PrivacySettings controller, add cache invalidation 2024-03-08 04:43:57 -07:00
Daniel Supernault 2e5e68e447
Update AP Profile Transformer, fix suspended attributes 2024-03-08 04:24:13 -07:00
Daniel Supernault 63100fe950
Update AP Profile Transformer, fix movedTo attribute 2024-03-08 03:56:53 -07:00
Daniel Supernault 25f3fa06af
Update AP Profile Transformer, add `suspended` attribute 2024-03-08 03:49:47 -07:00
daniel f09313a512
Merge pull request #4993 from pixelfed/staging
Update Curated Onboarding view, fix concierge form
2024-03-08 02:36:33 -07:00
Daniel Supernault 15ad69f76e
Update Curated Onboarding view, fix concierge form 2024-03-08 02:35:44 -07:00
Christian Winther 091de696c2 Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-03-07 19:43:30 +00:00
daniel 7c2ecd8706
Merge pull request #4992 from pixelfed/staging
Update SearchApiV2Service, use more efficient query
2024-03-07 03:34:10 -07:00
Daniel Supernault b89cd4c44f
Update changelog 2024-03-07 03:33:57 -07:00
Daniel Supernault cee618e844
Update SearchApiV2Service, use more efficient query 2024-03-07 03:33:18 -07:00
daniel 4516760ced
Merge pull request #4991 from pixelfed/staging
Update ApiV1Controller, use admin filter service
2024-03-07 03:13:13 -07:00
Daniel Supernault 94503a1cf9
Update ApiV1Controller, use admin filter service 2024-03-07 03:11:36 -07:00
daniel 5d7e091978
Merge pull request #4989 from pixelfed/staging
Staging
2024-03-07 02:40:25 -07:00
Daniel Supernault 18382e8a1f
Update DiscoverController, handle discover hashtag redirects 2024-03-07 02:38:50 -07:00
Daniel Supernault 592c84125c
Update StatusHashtagService, use more efficient cached count 2024-03-07 02:37:35 -07:00
daniel b6437380b4
Merge pull request #4988 from pixelfed/staging
Staging
2024-03-07 01:29:59 -07:00
Daniel Supernault e68fe64ffc
Update compiled assets 2024-03-07 01:29:31 -07:00
Daniel Supernault 3a27e637f8
Update Post.vue 2024-03-07 01:28:26 -07:00
Christian Winther dd5878b256 Allow setting the DB root password seperately
Fixes https://github.com/pixelfed/pixelfed/issues/4980
2024-03-06 20:49:36 +00:00
Christian Winther ae645ddd15 Use rsync container image that supports arm64 and amd64
fixes https://github.com/pixelfed/pixelfed/issues/4979
2024-03-06 20:45:04 +00:00
Christian Winther e38aa65dad keep building for jippi-fork branch 2024-03-06 19:38:34 +00:00
daniel 0355830c5c
Merge pull request #4976 from pixelfed/staging
Update SoftwareUpdateService, add command to refresh latest versions
2024-03-05 07:05:37 -07:00
Daniel Supernault eccdbe1f57
Update changelog 2024-03-05 07:03:51 -07:00
Daniel Supernault 632f2cb619
Update SoftwareUpdateService, add command to refresh latest versions 2024-03-05 07:02:40 -07:00
daniel 23f7b74400
Merge pull request #4975 from pixelfed/staging
Staging
2024-03-05 06:46:16 -07:00
Daniel Supernault b1cdf4464f
Update docker workflow 2024-03-05 06:38:00 -07:00
Daniel Supernault b122c60de7
Update gitignore 2024-03-05 06:31:46 -07:00
Daniel Supernault 6036d96e3f
Update changelog 2024-03-05 06:28:14 -07:00
Daniel Supernault ff150ca6c9
Update compiled assets 2024-03-05 06:27:56 -07:00
daniel fc8462d565
Merge pull request #4886 from mbliznikova/4882_informative_err_message_for_mixed_media_album
Added an informative UI error message for attempt to create a mixed media album
2024-03-05 06:24:30 -07:00
Daniel Supernault 6231994253
Update compiled assets 2024-03-05 06:14:31 -07:00
daniel 5d21bba7b5
Merge pull request #4969 from shleeable/patch-15
Update navbar.vue
2024-03-05 06:10:43 -07:00
Daniel Supernault d18824e719
Update checkpoint view, improve input autocomplete. Fixes #4959 2024-03-05 06:07:37 -07:00
Daniel Supernault d3f6c71b8e
Update changelog 2024-03-05 06:05:26 -07:00
daniel 0bd3e0ab80
Merge pull request #4844 from jippi/jippi-fork
Refactor Docker/Compose
2024-03-05 06:03:14 -07:00
Daniel Supernault 5fb26a78bc
Bump version to 0.11.13 2024-03-05 06:00:37 -07:00
daniel 1251bf532c
Merge pull request #4974 from pixelfed/staging
API fixes
2024-03-05 05:43:08 -07:00
Daniel Supernault 03165ea46f
Update changelog 2024-03-05 05:40:52 -07:00
Daniel Supernault 3b5500b3a5
Update ApiV1Controller, fix hashtag feed to include private posts from accounts you follow or your own, and your own unlisted posts 2024-03-05 05:39:47 -07:00
Daniel Supernault 1a811b1840
Update changelog 2024-03-05 04:44:02 -07:00
Daniel Supernault e3826c587d
Update ApiV1Controller, handle public feed parameter bug to gracefully fallback to min_id=1 when max_id=0 2024-03-05 04:43:21 -07:00
Daniel Supernault d6eac65555
Update ApiV1Controller, fix public timeline scope, properly support both local + remote parameters 2024-03-05 04:37:20 -07:00
daniel eb19c35343
Merge pull request #4973 from pixelfed/staging
Update ApiV1Controller, fix Notifications endpoint
2024-03-05 02:11:07 -07:00
Daniel Supernault 6cb1484b3e
cs fix 2024-03-05 01:58:15 -07:00
Daniel Supernault 01535a6cfe
Update ApiV1Controller, improve notification filtering 2024-03-05 01:56:16 -07:00
Daniel Supernault 31e6487dc9
Update changelog 2024-03-05 00:40:22 -07:00
Daniel Supernault a933615b8d
Update ApiV1Controller, update Notifications endpoint to filter notifications with missing activities 2024-03-05 00:39:50 -07:00
daniel a9b99d8f9d
Merge pull request #4972 from pixelfed/staging
Update ProfileMigration model, add target relation
2024-03-05 00:30:22 -07:00
Daniel Supernault 3f0539978e
Update ProfileMigration model, add target relation 2024-03-05 00:29:37 -07:00
daniel 712b6d27a9
Merge pull request #4968 from pixelfed/staging
Add Profile Migrations
2024-03-05 00:23:35 -07:00
Daniel Supernault 4a6be62128
Add account migration configurable, but enabled by default 2024-03-05 00:05:05 -07:00
Daniel Supernault 45bdfe1efd
Add Profile Migration federation 2024-03-04 23:16:32 -07:00
Shlee 7fd5599fc4
Update navbar.vue 2024-03-03 16:08:57 +10:30
Daniel Supernault 7613eec476
Update compiled assets 2024-03-02 04:24:56 -07:00
Daniel Supernault 9bc5338dbd
Update migration setting view 2024-03-02 04:23:48 -07:00
Daniel Supernault f8145a78cf
Add Profile Migrations 2024-03-02 04:21:04 -07:00
Christian Winther d92cf7f92f Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-29 21:34:36 +00:00
daniel 99611f90ea
Merge pull request #4964 from pixelfed/staging
Update AccountTransformer, fix follower/following count visibility bug
2024-02-29 05:00:55 -07:00
Daniel Supernault d5a6d9cc8d
Update changelog 2024-02-29 05:00:44 -07:00
Daniel Supernault 542d110673
Update AccountTransformer, fix follower/following count visibility bug 2024-02-29 04:59:13 -07:00
Daniel Supernault 402a4607c9
Update Inbox, fix flag validation condition, allow profile reports 2024-02-29 04:51:56 -07:00
Christian Winther 5d56460082 Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-29 10:46:45 +00:00
daniel 189e87f28a
Merge pull request #4962 from pixelfed/staging
Add Remote Reports to Admin Dashboard Reports page
2024-02-29 03:31:42 -07:00
Daniel Supernault c4190eec08
Update changelog 2024-02-29 03:27:46 -07:00
Daniel Supernault 0bb7d379c5
Update compiled assets 2024-02-29 03:27:28 -07:00
Daniel Supernault 372a116a2c
Add remote report components 2024-02-29 03:27:03 -07:00
Daniel Supernault ef0ff78e4a
Add Remote Reports to Admin Dashboard Reports page 2024-02-29 03:24:33 -07:00
Daniel Supernault ab9ecb6efd
Update AdminCuratedRegisterController, filter confirmation activities from activitylog 2024-02-29 02:04:43 -07:00
daniel e4f33e823d
Merge pull request #4961 from pixelfed/staging
Staging
2024-02-28 21:07:16 -07:00
Daniel Supernault 2f48df8ca8
Update kb, add email confirmation issues page 2024-02-28 21:06:21 -07:00
Christian Winther 6fa112162f Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-27 21:36:47 +00:00
Daniel Supernault a16309ac18
Update AdminReportController, add story report support 2024-02-26 21:39:09 -07:00
Daniel Supernault 767522a85c
Update AdminReports, add story reports and fix cs 2024-02-26 21:33:10 -07:00
daniel 8c1e136ce9
Merge pull request #4958 from pixelfed/staging
Add Curated Onboarding Templates
2024-02-26 21:05:24 -07:00
Daniel Supernault 071163b47b
Add Curated Onboarding Templates 2024-02-26 20:41:27 -07:00
Christian Winther acb699bf13 use the correct buildkit env for downloading binaries 2024-02-25 11:00:26 +00:00
Christian Winther 02369cce66 fix validation issues in the .env.docker file 2024-02-25 10:53:29 +00:00
Christian Winther c1c361ef9b tune for new dottie image 2024-02-24 23:29:45 +00:00
Christian Winther b08bb3669d bump dottie version 2024-02-24 23:00:38 +00:00
Christian Winther 1976af6dd1 ensure color in dottie output by passing through env 2024-02-24 22:50:48 +00:00
Christian Winther 020bda85db Merge remote-tracking branch 'pixelfed/staging' into jippi-fork 2024-02-24 21:42:13 +00:00
daniel 507f45f139
Merge pull request #4955 from pixelfed/staging
Update Curated Onboarding dashboard, improve application filtering an…
2024-02-24 03:50:37 -07:00
Daniel Supernault 795e91e3bc
Update changelog 2024-02-24 03:50:26 -07:00
Daniel Supernault 2b5d723582
Update Curated Onboarding dashboard, improve application filtering and make it easier to distinguish response state 2024-02-24 03:45:09 -07:00
daniel 7159d5cb3e
Merge pull request #4954 from pixelfed/staging
Update Inbox and StatusObserver, fix silently rejected direct message…
2024-02-23 19:40:29 -07:00
Daniel Supernault 84aeec3b4e
Update changelog 2024-02-23 19:37:55 -07:00
Daniel Supernault 089ba3c471
Update Inbox and StatusObserver, fix silently rejected direct messages due to saveQuietly which failed to generate a snowflake id 2024-02-23 19:37:02 -07:00
Christian Winther df1f62e734 update docs 2024-02-22 15:30:34 +00:00
Christian Winther c8c2e1c2eb update docs + paths 2024-02-22 15:24:51 +00:00
Christian Winther 2d8e81c83f ensure ownership of shared proxy conf 2024-02-22 15:14:32 +00:00
Christian Winther 515198b28c sync ignore files 2024-02-22 15:12:22 +00:00
Christian Winther f0e30c8ab6 expand docs for proxy nginx config 2024-02-22 15:02:53 +00:00
Christian Winther 7ffbd5d44a rename docker/bash to docker/shell 2024-02-22 14:58:03 +00:00
Christian Winther 5a43d7a65d improve error handling for [run-command-as] helper 2024-02-22 14:57:10 +00:00
Christian Winther 027f858d85 ensure correct ownership of ./storage/docker 2024-02-22 14:56:54 +00:00
Christian Winther e2821adcca fix spacing 2024-02-22 14:56:33 +00:00
Christian Winther af47d91e7d give nginx config default max upload size 2024-02-22 14:56:08 +00:00
Christian Winther 193d536ca1 update dottie 2024-02-22 14:54:20 +00:00
Christian Winther f264dd1cbb space redirects in shell scripts 2024-02-22 14:53:59 +00:00
Christian Winther d9d2a475d8 sort keys in compose 2024-02-22 14:53:39 +00:00
Christian Winther 8fd27c6f0c use remote build cache for faster local dev 2024-02-22 14:50:15 +00:00
Christian Winther 0addfe5605 allow .env control of a couple of PHP settings 2024-02-22 14:49:18 +00:00
Christian Winther 28b83b575f Bump dottie 2024-02-22 14:48:31 +00:00
Christian Winther 3bfd043792 update ignore files 2024-02-22 14:32:50 +00:00
Christian Winther 0ecebbb8bf push build cache to registry as well 2024-02-22 14:07:51 +00:00
Christian Winther 9c26bf26dd push build cache to registry as well 2024-02-22 13:40:32 +00:00
Christian Winther ae358e47cb push build cache to registry as well 2024-02-22 13:38:27 +00:00
Christian Winther 14f8478e6a push build cache to registry as well 2024-02-22 13:25:56 +00:00
Christian Winther 26d6f8f9fe push build cache to registry as well 2024-02-22 13:21:58 +00:00
daniel 36f84db03b
Merge pull request #4953 from pixelfed/staging
Staging
2024-02-22 03:51:39 -07:00
Daniel Supernault eadf2e9d1d
Update changelog 2024-02-22 03:51:10 -07:00
Daniel Supernault b0ecdc8162
Update compiled assets 2024-02-22 03:50:56 -07:00
Daniel Supernault 59c70239f8
Update Directory logic, add curated onboarding support 2024-02-22 03:39:13 -07:00
daniel b2f29a4590
Merge pull request #4952 from pixelfed/staging
Update AdminCuratedRegisterController, show oldest applications first
2024-02-22 01:55:20 -07:00
Daniel Supernault 4c5e8288b0
Update changelog 2024-02-22 01:55:08 -07:00
Daniel Supernault c4dde64119
Update AdminCuratedRegisterController, show oldest applications first 2024-02-22 01:54:18 -07:00
Christian Winther f486bfb73e add small dottie wrapper 2024-02-21 22:29:07 +00:00
Christian Winther adf1af3703 add small bash/artisan helper commands 2024-02-21 22:15:41 +00:00
Christian Winther abee7d4d62 add missing profiles 2024-02-21 21:52:59 +00:00
Christian Winther 5a9cfe1f2a Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-19 13:31:29 +00:00
daniel 1f6dc94a34
Merge pull request #4946 from pixelfed/staging
Add Curated Onboarding
2024-02-19 05:57:04 -07:00
Daniel Supernault e1715d40b8
Update changelog 2024-02-19 05:44:34 -07:00
Daniel Supernault 0ad3654da3
Update compiled assets 2024-02-19 04:35:11 -07:00
Daniel Supernault 06655c3a8b
Update LandingService, add curated onboarding parameter 2024-02-19 04:34:57 -07:00
Daniel Supernault cae26c666d
Update landing nav, fix curated onboarding state 2024-02-19 04:33:29 -07:00
Daniel Supernault 8355d5d00c
Add AdminCuratedRegisterController 2024-02-19 04:03:39 -07:00
Daniel Supernault 8dac2caf1d
Add Curated Onboarding 2024-02-19 04:00:31 -07:00
daniel 4057ee3bb3
Merge pull request #4944 from pixelfed/staging
Update ApiV1Controller, implement better limit logic to gracefully ha…
2024-02-19 01:48:28 -07:00
Daniel Supernault 9409c569bd
Update compiled assets 2024-02-19 01:47:57 -07:00
Daniel Supernault b6c97d1d26
Update npm deps 2024-02-19 01:46:45 -07:00
Daniel Supernault eb0e76f8e2
Update changelog 2024-02-19 01:39:52 -07:00
Daniel Supernault 1f74a95d0c
Update ApiV1Controller, implement better limit logic to gracefully handle requests with limits that exceed the max 2024-02-19 01:36:09 -07:00
daniel 6c0c61e45a
Merge pull request #4943 from pixelfed/staging
Staging
2024-02-18 22:19:21 -07:00
daniel 8cb7ebdd8b
Merge branch 'dev' into staging 2024-02-18 22:19:00 -07:00
Daniel Supernault ce9c0e0b24
Update changelog 2024-02-18 22:16:46 -07:00
Daniel Supernault 545f7d5e70
Update api v1/v2 instance endpoints, bump mastoapi version from 2.7.2 to 3.5.3 2024-02-18 22:14:23 -07:00
daniel f97afcf47e
Merge pull request #4938 from ThisIsMissEm/patch-2
Update .gitattributes to collapse diffs on generated files
2024-02-18 19:21:14 -07:00
daniel b339f4a5b0
Merge pull request #4942 from pixelfed/staging
Staging
2024-02-18 18:11:07 -07:00
Daniel Supernault 147113cc95
Update changelog 2024-02-18 18:02:08 -07:00
Daniel Supernault ea6b162340
Update cache config, use predis as default redis driver client 2024-02-18 18:01:14 -07:00
Daniel Supernault 4c26f59cd0
Update composer deps, add php 8.3 support 2024-02-18 18:00:43 -07:00
Christian Winther 9117df186c more validation fixes 2024-02-19 00:52:12 +00:00
Christian Winther 4dc15bb37d fix validation 2024-02-19 00:48:36 +00:00
daniel 4eb0c36480
Merge pull request #4941 from pixelfed/staging
Update federation config, increase default timeline days falloff to 9…
2024-02-18 13:08:00 -07:00
Daniel Supernault 17027c3487
Update changelog 2024-02-18 13:07:26 -07:00
Daniel Supernault 011834f473
Update federation config, increase default timeline days falloff to 90 days from 2 days. Fixes #4905 2024-02-18 13:05:49 -07:00
Christian Winther 9a1c4d42b5 drop php 8.1 support 2024-02-17 01:23:12 +00:00
Christian Winther 6edd712581 bump dottie 2024-02-17 01:19:59 +00:00
Christian Winther d4198b3262 Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-17 00:27:26 +00:00
Emelia Smith 9978b2b959
Update .gitattributes to collapse diffs on generated files 2024-02-16 17:56:13 +01:00
daniel 01a86009e6
Merge pull request #4936 from pixelfed/staging
Update Inbox, cast live filters to lowercase
2024-02-16 06:58:13 -07:00
Daniel Supernault d835e0adaa
Update Inbox, cast live filters to lowercase 2024-02-16 06:57:04 -07:00
daniel f45d293707
Merge pull request #4935 from pixelfed/staging
Bump version to 0.11.12
2024-02-16 03:09:23 -07:00
Daniel Supernault 66f6640072
Bump version to 0.11.12 2024-02-16 03:08:50 -07:00
daniel 25dd2e46fe
Merge pull request #4934 from pixelfed/staging
Add Software Update banner to admin home feeds
2024-02-16 03:02:36 -07:00
Daniel Supernault f07843a0f2
Update compiled assets 2024-02-16 03:01:51 -07:00
Daniel Supernault 56b736c325
Update changelog 2024-02-16 03:00:51 -07:00
Daniel Supernault b0fb198829
Add Software Update banner to admin home feeds 2024-02-16 02:58:27 -07:00
daniel e1d579b10b
Merge pull request #4933 from pixelfed/staging
Autospam Live Filters - block remote activities based on comma separated keywords
2024-02-16 02:20:23 -07:00
Daniel Supernault 83eadbb811
Update changelog 2024-02-16 02:20:11 -07:00
Daniel Supernault 40b45b2a11
Update Autospam, add live filters to block remote activities based on comma separated keywords 2024-02-16 02:15:39 -07:00
daniel ccbba91e70
Merge pull request #4932 from pixelfed/staging
Update routes
2024-02-15 23:36:51 -07:00
Daniel Supernault bc4d223714
Update routes 2024-02-15 22:20:40 -07:00
daniel 0032415459
Merge pull request #4931 from pixelfed/staging
Staging
2024-02-15 21:42:15 -07:00
Daniel Supernault 70fc44dfe5
Update changelog 2024-02-15 21:41:40 -07:00
Daniel Supernault 0f3ca19461
Update status view, fix unlisted/private scope bug 2024-02-15 21:41:18 -07:00
daniel 0dc54e9ac0
Merge pull request #4930 from pixelfed/staging
Staging
2024-02-15 21:23:29 -07:00
Daniel Supernault df5e61266c
Update changelog 2024-02-15 21:23:00 -07:00
Daniel Supernault 1232cfc86a
Update ActivityPubFetchService, enforce stricter Content-Type validation 2024-02-15 21:22:41 -07:00
daniel af935c729b
Merge pull request #4929 from pixelfed/staging
Staging
2024-02-15 20:59:35 -07:00
Daniel Supernault 4c6ec20e36
Update changelog 2024-02-15 20:59:08 -07:00
Daniel Supernault fb0bb9a34f
Update Federation, use proper Content-Type headers for following/follower collections 2024-02-15 20:58:43 -07:00
Christian Winther d3bbfdb6e0 Merge branch 'staging' of github.com:jippi/pixelfed into jippi-fork 2024-02-13 00:52:18 +00:00
daniel b10c60584b
Merge pull request #4924 from pixelfed/staging
Update public/network timelines, fix non-redis response and fix reblo…
2024-02-11 20:26:31 -07:00
Daniel Supernault 221fe43638
Update compiled assets 2024-02-11 20:25:18 -07:00
Daniel Supernault 97c131fdf2
Update AccountImport component 2024-02-11 20:24:27 -07:00
Daniel Supernault 78da12004f
Update changelog 2024-02-11 20:23:19 -07:00
Daniel Supernault 8b4ac5cc0b
Update public/network timelines, fix non-redis response and fix reblogs in home feed 2024-02-11 20:21:46 -07:00
daniel bbd3688333
Merge pull request #4923 from pixelfed/staging
Update ApiV1Controller, fix network timeline
2024-02-11 14:30:46 -07:00
Daniel Supernault 7b8977e9cc
Update changelog 2024-02-11 14:27:38 -07:00
Daniel Supernault 0faf59e3b7
Update ApiV1Controller, fix network timeline 2024-02-11 14:26:45 -07:00
Christian Winther 49a778d128 add CODEOWNERS 2024-02-11 02:00:09 +00:00
Christian Winther fd62962d20 delete contrib 2024-02-11 01:57:11 +00:00
Christian Winther e18d6083a2 bump dottie 2024-02-11 01:24:26 +00:00
Christian Winther 143d5703dd update .env.docker 2024-02-10 23:08:22 +00:00
Christian Winther bc66b6da18 many small fixes and improvements 2024-02-10 20:03:04 +00:00
Christian Winther d8e1caec53 Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork 2024-02-10 10:44:02 +00:00
daniel 8fa6ae421b
Merge pull request #4915 from pixelfed/staging
Bump version to v0.11.11
2024-02-09 20:58:43 -07:00
Daniel Supernault e5bbe9340a
Bump version to v0.11.11 2024-02-09 20:57:45 -07:00
Daniel Supernault 7a7b4bc717
Update AuthServiceProvider 2024-02-09 20:54:17 -07:00
daniel 8ab9951909
Merge pull request #4914 from pixelfed/staging
Fix api endpoints
2024-02-09 20:52:09 -07:00
Daniel Supernault 62b9eef805
Fix api endpoints 2024-02-09 20:51:37 -07:00
daniel 67167a5b90
Merge pull request #4913 from pixelfed/staging
Fix api endpoints
2024-02-09 20:45:44 -07:00
Daniel Supernault fd7f5dbba1
Fix api endpoints 2024-02-09 20:45:10 -07:00
daniel 0649bb4754
Merge pull request #4912 from pixelfed/staging
Fix api endpoints
2024-02-09 20:41:42 -07:00
Daniel Supernault e354750808
Fix api endpoints 2024-02-09 20:41:12 -07:00
daniel 0dbbc6a6b4
Merge pull request #4911 from pixelfed/staging
Staging
2024-02-09 20:06:20 -07:00
Daniel Supernault 607b239c1a
Bump version to v0.11.10 2024-02-09 20:05:22 -07:00
Daniel Supernault 2e6100f275
Update BearerTokenResponse, fix scope bug 2024-02-09 20:04:15 -07:00
daniel 7e47d6dccb
Merge pull request from GHSA-gccq-h3xj-jgvf
[staging] Implement proper OAuth authorization on API endpoints
2024-02-09 19:44:27 -07:00
Christian Winther d9a9507cc8 sync 2024-02-10 00:30:06 +00:00
Christian Winther 5bd93b0f5e sync 2024-02-10 00:25:18 +00:00
Christian Winther 3d6efd098d sync 2024-02-10 00:23:53 +00:00
Emelia Smith 0f8e45fe75
Implement proper OAuth authorization on API endpoints 2024-02-09 02:28:08 +01:00
Emelia Smith 9330cd02f7
Implement proper OAuth authorization on Admin API endpoints 2024-02-09 02:28:08 +01:00
Emelia Smith 7b0a6060b2
Return access tokens' scopes, not hardcoded list 2024-02-09 02:28:08 +01:00
Christian Winther d374d73ba7
Merge branch 'staging' into jippi-fork 2024-02-08 01:17:25 +01:00
daniel 73b4dab9a8
Merge pull request #4904 from pixelfed/staging
Update ApiV2Controller, add vapid key to instance object. Thanks this…
2024-02-07 06:00:20 -07:00
Daniel Supernault 2becd273c4
Update changelog 2024-02-07 06:00:09 -07:00
Daniel Supernault 4d02d6f12e
Update ApiV2Controller, add vapid key to instance object. Thanks thisismissem! 2024-02-07 05:59:12 -07:00
daniel a1b0d3d3c0
Merge pull request #4903 from pixelfed/staging
Add migration
2024-02-07 04:50:33 -07:00
Daniel Supernault 97b7cb2719
Add migration 2024-02-07 04:49:44 -07:00
daniel 6ea20716bc
Merge pull request #4902 from pixelfed/staging
Staging
2024-02-07 04:45:47 -07:00
Daniel Supernault 1f3f0cae65
Update changelog 2024-02-07 04:43:32 -07:00
Daniel Supernault 6921d3568e
Add InstanceMananger command 2024-02-07 04:42:27 -07:00
Daniel Supernault 5b284cacea
Update ApiV1Controller, enforce blocked instance domain logic 2024-02-07 04:41:12 -07:00
Daniel Supernault 01b33fb37e
Update PublicApiController, consume InstanceService blocked domains for account and statuses endpoints 2024-02-07 03:43:20 -07:00
Daniel Supernault 1e3acadefb
Update horizon.php config 2024-02-07 02:52:37 -07:00
Daniel Supernault ac01f51ab6
Update FetchNodeinfoPipeline, use more efficient dispatch 2024-02-07 02:51:50 -07:00
Daniel Supernault 289cad470b
Update Instance model, add entity casts 2024-02-07 02:49:29 -07:00
Daniel Supernault 240e6bbe4f
Update NodeinfoService, disable redirects 2024-02-07 02:47:34 -07:00
daniel 111ba70473
Merge pull request #4897 from pixelfed/staging
Staging
2024-02-04 07:20:53 -07:00
Daniel Supernault 80e0ada946
Update changelog 2024-02-04 07:18:54 -07:00
Daniel Supernault fa97a1f38e
Update notification pipelines, fix non-local saving 2024-02-04 07:18:05 -07:00
Daniel Supernault 4d4013896c
Update NotificationEpochUpdatePipeline, use more efficient query 2024-02-04 07:11:05 -07:00
daniel 3a557d7ffc
Merge pull request #4896 from pixelfed/staging
Update AP helpers, fix sensitive bug
2024-02-04 03:19:08 -07:00
Daniel Supernault 152b6eab9a
Update changelog 2024-02-04 03:18:58 -07:00
Daniel Supernault 00ed330cf3
Update AP helpers, fix sensitive bug 2024-02-04 03:16:57 -07:00
daniel 2483832754
Merge pull request #4895 from pixelfed/staging
Update AP helpers, refactor post count decrement logic
2024-02-04 03:03:16 -07:00
Daniel Supernault 09ca96cc2b
Update changelog 2024-02-04 02:54:37 -07:00
Daniel Supernault 8b843d620c
Update ProfilePipeline jobs 2024-02-04 02:53:40 -07:00
Daniel Supernault b81ae5773f
Update AP helpers, refactor post count decrement logic 2024-02-04 02:50:48 -07:00
daniel 5d89fe8130
Merge pull request #4894 from pixelfed/staging
Update AP helpers, more efficently update post counts
2024-02-04 02:41:08 -07:00
Daniel Supernault ddf7f09ad4
Update changelog 2024-02-04 02:40:59 -07:00
Daniel Supernault 7caed381fb
Update AP helpers, more efficently update post counts 2024-02-04 02:40:04 -07:00
daniel 3d5fd48a22
Merge pull request #4893 from pixelfed/staging
Update TransformImports command, fix import service condition
2024-02-03 13:31:21 -07:00
Daniel Supernault d3ff89e538
Update changelog 2024-02-03 13:31:11 -07:00
Daniel Supernault 32c59f0440
Update TransformImports command, fix import service condition 2024-02-03 13:30:02 -07:00
daniel 4050055e5e
Merge pull request #4891 from pixelfed/staging
Add S3 IG Import Media Storage
2024-02-02 05:50:09 -07:00
Daniel Supernault 04c5e550a5
Update changelog 2024-02-02 05:46:28 -07:00
Daniel Supernault 081360b905
Update console kernel, add ig import s3 job 2024-02-02 05:45:06 -07:00
Daniel Supernault edbb07cc37
Add import video thumbnail job 2024-02-02 05:30:32 -07:00
Daniel Supernault 622e9cee97
Add S3 IG Import Media Storage 2024-02-02 02:29:33 -07:00
daniel 9dcb25c8e2
Merge pull request #4890 from pixelfed/staging
Update AccountImport.vue, fix new IG export format
2024-02-01 23:21:03 -07:00
Daniel Supernault 5b7111c56f
Update changelog 2024-02-01 23:20:12 -07:00
Daniel Supernault cf00542336
Update compiled assets 2024-02-01 23:19:25 -07:00
Daniel Supernault 59aa6a4b02
Update AccountImport.vue, fix new IG export format 2024-02-01 22:49:04 -07:00
daniel 33d1faf734
Merge pull request #4889 from pixelfed/staging
Staging
2024-02-01 22:47:18 -07:00
Daniel Supernault 339857ffa2
Update changelog 2024-02-01 22:46:39 -07:00
Daniel Supernault 0aff126aa0
Update ApiV1Controller, properly cast boolean sensitive parameter. Fixes #4888 2024-02-01 22:46:13 -07:00
mbliznikova fd4f41a14e Added an informative UI error message for attempt to create a mixed media album 2024-01-30 19:19:25 +00:00
daniel 29785b5654
Merge pull request #4884 from pixelfed/staging
Staging
2024-01-29 22:25:32 -07:00
Daniel Supernault 8a9a7c0e47
Fix parental_controls migration 2024-01-29 22:24:50 -07:00
Daniel Supernault 61b1523368
Fix newsroom migration 2024-01-29 22:13:09 -07:00
Daniel Supernault 92ff114d2d
Update migrations, fixes #4883 2024-01-29 21:45:59 -07:00
daniel 6d8ba64e0b
Merge pull request #4879 from shleeable/patch-14
typo
2024-01-26 20:57:10 -07:00
daniel 44f92f4888
Merge pull request #4856 from nexryai/patch-1
Fix syntax error of lang/vendor/backup/ja/notifications.php
2024-01-26 20:56:35 -07:00
daniel d4e4c4e1dd
Merge pull request #4859 from mbliznikova/4845_provide_error_message_when_account_size_limit_reached
Provide an informative error message when account size limit is reached
2024-01-26 20:55:44 -07:00
Christian Winther 2aeccf885f test: remove slash in tags 2024-01-27 00:01:06 +00:00
Christian Winther 043f914c8c test: change prefix for docker tags 2024-01-26 23:58:38 +00:00
Christian Winther 3723f36043 remove unneeded workflow triggers 2024-01-26 23:54:24 +00:00
Christian Winther ef37c8f234 name CI jobs 2024-01-26 23:48:04 +00:00
Christian Winther b73d452255 sort ARG in Dockerfile 2024-01-26 23:39:18 +00:00
Christian Winther 36850235a8 Merge remote-tracking branch 'origin/staging' into jippi-fork 2024-01-26 22:57:23 +00:00
Christian Winther 1a6e97c98b try to make 8.3 build working by building imagick from master branch 2024-01-26 22:51:15 +00:00
Christian Winther 8bdb0ca77b fix directory-is-empty and add tests to avoid regressions 2024-01-26 21:22:43 +00:00
Christian Winther c4f984b205 remove php extension FTP requirement 2024-01-26 20:46:09 +00:00
Christian Winther 1616c7cb11 make directory-is-empty more robust 2024-01-26 20:45:56 +00:00
Christian Winther ca5710b5ae fix 12-migrations.sh properly detecting new migrations and printing output 2024-01-26 20:45:38 +00:00
Christian Winther a665168031 change where overrides are placed 2024-01-26 20:45:08 +00:00
Christian Winther 335e6954d2 remove noisy log statements in as-boolean 2024-01-26 20:19:50 +00:00
Christian Winther 5c208d0519 allow easy overrides of any and all files in container via new override mount 2024-01-26 20:19:34 +00:00
Christian Winther aa2669c327 remove invalid/confusing statement in migrations about running migrations when it isn't enabled 2024-01-26 20:18:29 +00:00
Christian Winther 8189b01a26 improve naming of directory-is-empty 2024-01-26 20:17:54 +00:00
Christian Winther d372b9dee7 Set stop_signal for worker to stop Horizon more correct 2024-01-26 20:15:57 +00:00
Christian Winther d2ed117d3f improve Dockerfile for composer.json+composer.lock 2024-01-26 20:15:33 +00:00
Christian Winther 8d61b8d250 add Docker as recommended vscode plugin 2024-01-26 20:14:59 +00:00
Christian Winther c859367e10 fix 02-check-config.sh logic and bad .env.docker syntax 2024-01-26 20:14:40 +00:00
Christian Winther 6fee842b7a also build and push staging images 2024-01-26 18:24:15 +00:00
Christian Winther 627fffd1ce add .vscode with recommended plugins + settings
which will give a *great* out of the box experience for folks wanting to contribute and uses VS Code
2024-01-26 14:42:24 +00:00
Christian Winther f263dfc4e1 apply editorconfig + shellcheck + shellfmt to all files 2024-01-26 14:41:44 +00:00
Shlee 934f2ffdb4
Update home.blade.php 2024-01-24 12:45:21 +10:30
Christian Winther c9a3e3aea7 automatically + by default turn off proxy acme if proxy is off 2024-01-23 19:34:48 +00:00
Christian Winther 8672453596 fix configuration loading before referencing config 2024-01-22 13:48:01 +00:00
daniel 51c6935e05
Merge pull request #4877 from pixelfed/staging
Update UserEmailForgotController
2024-01-22 06:00:37 -07:00
Daniel Supernault 6167ebc654
Update UserEmailForgotController 2024-01-22 05:59:37 -07:00
daniel c77e427fa3
Merge pull request #4876 from pixelfed/staging
Update forgot mail template, urlencode email
2024-01-22 05:58:15 -07:00
Daniel Supernault efe8e89046
Update forgot mail template, urlencode email 2024-01-22 05:57:40 -07:00
daniel 1f6577c947
Merge pull request #4875 from pixelfed/staging
Update forgot email captcha config
2024-01-22 05:52:27 -07:00
Daniel Supernault 96366ab3de
Update forgot email captcha config 2024-01-22 05:51:50 -07:00
daniel dbf59367df
Merge pull request #4874 from pixelfed/staging
Add forgot email feature
2024-01-22 05:42:39 -07:00
Daniel Supernault c26a3d2817
Add migration 2024-01-22 05:35:30 -07:00
Daniel Supernault 5afe7abdfb
Update changelog 2024-01-22 05:26:57 -07:00
Daniel Supernault 67c650b195
Add forgot email feature 2024-01-22 05:26:01 -07:00
Daniel Supernault 0325e17115
Update LoginController, fix captcha validation error message 2024-01-22 03:06:14 -07:00
daniel ca05279bb6
Merge pull request #4872 from pixelfed/staging
Update login view, add email prefill logic
2024-01-22 02:05:43 -07:00
Daniel Supernault 74423b52ca
Update changelog 2024-01-22 02:05:01 -07:00
Daniel Supernault d76f01685c
Update login view, add email prefill logic 2024-01-22 02:03:39 -07:00
Christian Winther 347ac6f82b fix Dockerfile indent 2024-01-18 17:33:24 +00:00
Christian Winther 70f4bc06a8 remove unsuded .gitmodules 2024-01-18 17:29:15 +00:00
Christian Winther a940bedf9e reference docs PR 2024-01-18 16:23:05 +00:00
Christian Winther 2d223d61ed remove docs
they now live in https://github.com/pixelfed/docs-next/pull/1

and https://jippi.github.io/pixelfed-docs-next/pr-preview/pr-1/running-pixelfed/
2024-01-18 16:22:26 +00:00
Christian Winther f2b28ece6e longer entrypoint bars 2024-01-17 18:26:04 +00:00
Christian Winther 3598f9f8f4 move check-config to after fix-permissions 2024-01-17 18:24:35 +00:00
Christian Winther 29564a5809 docs tuning 2024-01-17 18:20:45 +00:00
Christian Winther 033db841f4 try github alerts 2024-01-17 18:19:04 +00:00
Christian Winther a383233710 try github alerts 2024-01-17 18:12:30 +00:00
Christian Winther 32ad4266d0 try github alerts 2024-01-17 18:10:12 +00:00
Christian Winther 1500791198 try github alerts 2024-01-17 18:07:45 +00:00
Christian Winther e858a453be try github alerts 2024-01-17 18:06:43 +00:00
Christian Winther 4729ffb7d5 try github alerts 2024-01-17 18:05:02 +00:00
Christian Winther 83d92c4819 try github alerts 2024-01-17 18:04:08 +00:00
Christian Winther eba2db76f2 try github alerts 2024-01-17 18:03:18 +00:00
Christian Winther a3fd373796 try github alerts 2024-01-17 18:02:38 +00:00
Christian Winther 98bae1316f cleanup .env.docker variable names and placement in the file 2024-01-17 17:51:37 +00:00
Christian Winther 068143639f fix gitignore 2024-01-17 16:45:05 +00:00
Christian Winther f135a240cd fix color 2024-01-17 16:41:01 +00:00
Christian Winther a094a0bd66 syntax fix 2024-01-17 16:30:40 +00:00
Christian Winther dc95d4d800 colors 2024-01-17 16:29:15 +00:00
Christian Winther ca0a25912a more color tuning 2024-01-17 16:25:34 +00:00
Christian Winther 62efe8b3d4 ensure default health check values 2024-01-17 16:18:29 +00:00
Christian Winther ead7c33275 more docker config tuning 2024-01-17 16:11:36 +00:00
Christian Winther cc9f673eea test proxy via direct url 2024-01-17 16:01:52 +00:00
Christian Winther 921f34d42e color tweaks 2024-01-17 15:59:58 +00:00
Christian Winther 82ab545f1a more clear separation between log entry points 2024-01-17 15:55:05 +00:00
Christian Winther eee17fe9f2 harden proxy health check to be https based 2024-01-17 15:53:17 +00:00
Christian Winther adbd66eb38 fix defaults 2024-01-17 15:52:09 +00:00
Christian Winther 3feb93b034 cleanup color output 2024-01-17 15:49:49 +00:00
Christian Winther a4646df8f2 add some health checks 2024-01-17 15:47:39 +00:00
Christian Winther 2d05eccb87 add bats testing 2024-01-17 15:37:12 +00:00
Christian Winther e70e13e265 possible fix is-false/true logic 2024-01-17 14:52:22 +00:00
Christian Winther 90c9d8b5a6 more toned down colors 2024-01-17 14:50:29 +00:00
Christian Winther 9ad04a285a fix missing output colors 2024-01-17 14:48:02 +00:00
Christian Winther 3a7fd8eac9 fix default variables 2024-01-17 14:46:07 +00:00
Christian Winther 45f1df78b0 update proxy-acme paths 2024-01-17 14:41:48 +00:00
Christian Winther 44266b950b conditionally initialize passport and instance actor 2024-01-17 14:29:24 +00:00
Christian Winther be2ba79dc2 bugfixes 2024-01-17 14:25:31 +00:00
Christian Winther d8b37e6870 debug redis 2024-01-17 14:13:38 +00:00
Christian Winther 6563d4d0b9 add goss (https://github.com/goss-org/goss) validation 2024-01-17 13:49:56 +00:00
Christian Winther afa335b7b5 add missing pecl back 2024-01-17 12:50:55 +00:00
Christian Winther a70f108616 fix shellcheck error 2024-01-16 20:53:54 +00:00
Christian Winther bb960fd485 Merge branch 'jippi-fork' of github.com:jippi/pixelfed into jippi-fork 2024-01-16 20:51:49 +00:00
Christian Winther 88ad5d6a4f ignore some shellchecks for .env files 2024-01-16 20:51:37 +00:00
Christian Winther 24220ef2a8
Merge branch 'pixelfed:dev' into jippi-fork 2024-01-16 21:49:51 +01:00
daniel 84b63f8aa9
Merge pull request #4866 from pixelfed/staging
Update migration, fixes #4863
2024-01-15 22:43:38 -07:00
Daniel Supernault 979aa55135
Update migration, fixes #4863 2024-01-15 22:43:09 -07:00
Christian Winther daba285ea7 tune-up 2024-01-15 23:54:41 +00:00
Christian Winther de96c5f06d migration docs 2024-01-15 23:50:16 +00:00
Christian Winther 72b454143b tweaking configs 2024-01-15 20:42:11 +00:00
Christian Winther af1df5edfd ooops 2024-01-15 20:24:02 +00:00
Christian Winther 2135199c97 tune the github workflow config 2024-01-15 20:19:04 +00:00
Christian Winther 9c426b48a1 more docs 2024-01-15 19:56:35 +00:00
Christian Winther 48e5d45b3f improve faq 2024-01-15 19:43:52 +00:00
Christian Winther 98660760c9 improve faq 2024-01-15 19:39:59 +00:00
Christian Winther 53eb9c11fc add faq 2024-01-15 19:20:22 +00:00
Christian Winther 903aeb7608 more cleanup 2024-01-15 18:53:54 +00:00
Christian Winther 685f62a5d0 allow skipping one-time setup tasks 2024-01-15 18:44:43 +00:00
Christian Winther 7f99bb1024 implement automatic shellcheck linting 2024-01-15 17:30:14 +00:00
Christian Winther fa10fe999e implement automatic shellcheck linting 2024-01-15 17:25:42 +00:00
Christian Winther b2d6d3dbe7 implement automatic shellcheck linting 2024-01-15 17:23:32 +00:00
Christian Winther f2f2517503 implement automatic shellcheck linting 2024-01-15 17:17:48 +00:00
Christian Winther ed0f9d64c8 implement automatic shellcheck linting 2024-01-15 17:16:00 +00:00
Christian Winther 901d11df60 more docs help 2024-01-15 16:16:58 +00:00
Christian Winther 20ef1c7b94 backfil docs 2024-01-15 16:13:59 +00:00
Christian Winther 20a15c2b65 split up docs into smaller docs 2024-01-15 16:09:07 +00:00
Christian Winther 01ecde1592 allow skipping one-time setup tasks 2024-01-15 15:32:29 +00:00
Christian Winther 9814a39fd8 more docs 2024-01-15 15:14:44 +00:00
Christian Winther 519704cbe8 more tuning 2024-01-15 14:57:40 +00:00
Christian Winther 543dac34f6 update path 2024-01-15 14:48:12 +00:00
Christian Winther edbc1e4d60 expand docs 2024-01-15 14:44:47 +00:00
Christian Winther c258a15761 cleanup a bit 2024-01-15 14:42:54 +00:00
Christian Winther 84c9aeb514 fixing postgresql and some more utility help 2024-01-15 14:16:54 +00:00
Christian Winther 73b6db168a
Merge branch 'pixelfed:dev' into jippi-fork 2024-01-15 13:17:02 +01:00
daniel 187d1e1af9
Merge pull request #4862 from pixelfed/staging
Add Parental Controls feature
2024-01-11 07:08:38 -07:00
Daniel Supernault 85a612742d
Update changelog 2024-01-11 06:54:20 -07:00
Daniel Supernault c91f1c595a
Update ParentalControlsController, prevent children from adding accounts 2024-01-11 06:52:12 -07:00
Daniel Supernault db1b466792
Update instance config 2024-01-11 06:45:32 -07:00
Daniel Supernault c7ed684a5c
Update ParentalControlsController 2024-01-11 06:31:19 -07:00
Daniel Supernault 71c148c61e
Update StoryController, add parental controls support 2024-01-11 05:46:02 -07:00
Daniel Supernault fe30cd25d1
Update DirectMessageController, add parental controls support 2024-01-11 05:25:23 -07:00
Daniel Supernault fd9b5ad443
Update api controllers, add parental control support 2024-01-11 04:50:11 -07:00
Daniel Supernault 9d365d07f9
Update ParentalControls, map updated saved permissions/roles 2024-01-11 04:41:38 -07:00
Daniel Supernault 2dcfc81495
Update ComposeController, add parental controls support 2024-01-11 04:40:25 -07:00
Daniel Supernault 1a16ec2078
Update BookmarkController, add parental control support 2024-01-11 03:22:35 -07:00
Daniel Supernault 42298a2e9c
Apply dangerZone middleware to parental controls routes 2024-01-11 02:40:52 -07:00
Daniel Supernault 58745a8808
Update settings sidebar 2024-01-11 02:37:13 -07:00
Daniel Supernault 5f6ed85770
Update settings sidebar 2024-01-11 02:34:43 -07:00
Daniel Supernault 319a20b473
Update ParentalControlsController, redirect to new custom error page on active session when attempting to use child invite link so as to not overwrite parent active session with child session 2024-01-11 02:12:54 -07:00
Daniel Supernault ef57d471e5
Update migration 2024-01-11 01:50:51 -07:00
Daniel Supernault c53894fe16
Add Parental Controls feature 2024-01-11 01:35:15 -07:00
mbliznikova 4e567e3411 Provide an informative error message when account size limit is reached 2024-01-09 04:49:01 +00:00
Christian Winther 6f0a6aeb3d fix hadolint path 2024-01-07 14:54:28 +00:00
nexryai 19e8037c85
Fix lang/vendor/backup/ja/notifications.php 2024-01-07 21:05:04 +09:00
daniel 0a556d1ac1
Merge pull request #4855 from pixelfed/staging
Update ApiV1Controller, update favourites max limit. Fixes #4854
2024-01-06 11:45:12 -07:00
Daniel Supernault d25209f74a
Update ApiV1Controller, update favourites max limit. Fixes #4854 2024-01-06 11:43:56 -07:00
Christian Winther 2e3c7e862c iterating on proxy + letsencrypt setup 2024-01-06 18:01:48 +00:00
Christian Winther 284bb26d92 sync 2024-01-06 16:43:48 +00:00
Christian Winther 9445980e04 expose both http and https ports 2024-01-06 15:57:20 +00:00
Christian Winther bd1cd9c4fc more docs 2024-01-06 15:39:30 +00:00
Christian Winther e228a1622d refactor layout 2024-01-06 14:19:36 +00:00
Christian Winther c9b11a4a29 remove testing key 2024-01-06 14:13:16 +00:00
Christian Winther 092f7f704c fix nginx? 2024-01-06 00:01:51 +00:00
Christian Winther 6edf266a14 quick take on applying migrations automatically 2024-01-05 23:54:17 +00:00
Christian Winther a8c5585e19 use upstream Docker images over self-built 2024-01-05 23:41:33 +00:00
Christian Winther a25b7910b2 first time setup and more refinements 2024-01-05 23:16:26 +00:00
Christian Winther 7db513b366 sync 2024-01-05 18:16:38 +00:00
Christian Winther 76e1199dc7 sync 2024-01-05 17:35:07 +00:00
Christian Winther 2e2ffc5519 comment build steps out to use remote image 2024-01-05 17:31:34 +00:00
Christian Winther d876533991 remove tmp token 2024-01-05 17:30:30 +00:00
Christian Winther c4404590f2 add first time setup logic 2024-01-05 17:29:45 +00:00
Christian Winther c1fbccb07c bootstrapping worked 2024-01-05 16:52:00 +00:00
Christian Winther 052c11882c tweak 10-storage.sh 2024-01-05 16:33:08 +00:00
Christian Winther 215b49ea3d rename2 2024-01-05 16:27:11 +00:00
Christian Winther 10674ac523 iterate on apache example with docker-compose 2024-01-05 16:18:48 +00:00
Christian Winther f2eb3df85f remove VOLUME and EXPOSE
see https://stackoverflow.com/a/52571354/1081818
2024-01-05 01:34:46 +00:00
Christian Winther 5cfd8e15a9 quotes 2024-01-05 00:16:36 +00:00
Christian Winther 99e2a045a6 more renaming for clarity 2024-01-05 00:11:20 +00:00
Christian Winther d13895a3e0 add 15-storage-permissions.sh to the docs 2024-01-04 23:15:46 +00:00
Christian Winther 895b51fd9f more tweaks 2024-01-04 23:04:25 +00:00
Christian Winther 890827d60e Merge branch 'dev' of github.com:pixelfed/pixelfed into jippi-fork 2024-01-04 22:34:57 +00:00
Christian Winther c12ef66c56 opt-in fixing of user/group ownership of files 2024-01-04 22:33:41 +00:00
Christian Winther c64571e46d more docs 2024-01-04 22:16:25 +00:00
Christian Winther f2c8497136 more clanup 2024-01-04 21:55:24 +00:00
Christian Winther ce34e4d046 more docs and rework 2024-01-04 21:21:00 +00:00
Christian Winther a08a5e7cde more docs and rework 2024-01-04 20:55:04 +00:00
Christian Winther e05575283a update docs 2024-01-04 16:12:18 +00:00
Christian Winther c369ef50a7 more refactoring for templating 2024-01-04 16:08:01 +00:00
Christian Winther 7dcca09c65 a bit of refactoring 2024-01-04 13:07:01 +00:00
Christian Winther 7b3e11012f merge dev 2024-01-04 11:33:54 +00:00
Christian Winther 0aee66810d fix editorconfig 2024-01-04 11:28:00 +00:00
Christian Winther 6244511cf8 don't hardcode UID/GID for runtime 2024-01-04 11:20:22 +00:00
Christian Winther f390c3c3e9 install all database extensions by default
lifted from https://github.com/pixelfed/pixelfed/pull/4172
2024-01-04 11:11:16 +00:00
Christian Winther cf080dda09 rename init files 2024-01-04 11:01:56 +00:00
Christian Winther b19d3a20dd only run kernel tasks on one server
lifted from https://github.com/pixelfed/pixelfed/pull/4634
2024-01-04 11:00:45 +00:00
daniel d8a5dc00bb
Merge pull request #4847 from pixelfed/staging
Staging
2024-01-03 04:13:11 -07:00
Daniel Supernault bca2484994
Update Webfinger util, add avatar entity. Fixes #1629 2024-01-03 04:11:29 -07:00
daniel 5f5cb0616d
Merge pull request #3919 from shleeable/patch-3
Update NotificationCard.vue to popover image preview on comment/share
2024-01-03 02:59:56 -07:00
daniel d7efe1a7ee
Merge pull request #3894 from vanlueckn/feat-add-emoji-cli
Add a command to import emoji archives to pixelfed
2024-01-03 02:54:43 -07:00
daniel d0f7865508
Merge pull request #4846 from pixelfed/staging
Update meta tags, improve descriptions and seo/og tags
2024-01-03 02:03:19 -07:00
Daniel Supernault 5087a87885
Update changelog 2024-01-03 02:00:47 -07:00
Daniel Supernault fd44c80ce9
Update meta tags, improve descriptions and seo/og tags 2024-01-03 01:54:30 -07:00
daniel 5b4214cb80
Merge pull request #4843 from pixelfed/staging
Add Roles & Parental Controls
2024-01-02 23:05:23 -07:00
Daniel Supernault 0ef6812709
Update UserRoleService, add useDefaultFallback parameter 2024-01-02 22:07:42 -07:00
Daniel Supernault cbe75ce871
Update UserRolesController 2024-01-02 22:06:54 -07:00
Daniel Supernault 75b0f2dda0
Update ComposeController, add permissions check 2024-01-02 22:06:18 -07:00
Daniel Supernault d39946b045
Update ApiV1Controller, add permissions check 2024-01-02 22:04:27 -07:00
Daniel Supernault 7b6c9c7428
Update migrations 2024-01-01 16:19:24 -07:00
Christian Winther 98211d3620 refactor Dockerfile and Docker workflow 2023-12-28 23:46:59 +00:00
Daniel Supernault 7dbdbf15a5
Add Roles & Parental Controls 2023-12-27 02:51:47 -07:00
daniel 238f646306
Merge pull request #4842 from pixelfed/staging
Update AP ProfileTransformer, add published attribute
2023-12-25 00:31:15 -07:00
Daniel Supernault f66b9fe74e
Update changelog 2023-12-25 00:31:05 -07:00
Daniel Supernault adfaa2b140
Update AP ProfileTransformer, add published attribute 2023-12-25 00:30:05 -07:00
daniel 25a4289dc3
Merge pull request #4836 from pixelfed/staging
Fix StatusHashtag delete bug
2023-12-21 06:19:43 -07:00
Daniel Supernault 1be21c76f3
Fix StatusHashtag delete bug 2023-12-21 06:18:51 -07:00
daniel eebed73a5e
Merge pull request #4834 from pixelfed/staging
Add User Domain Blocks
2023-12-21 05:25:46 -07:00
Daniel Supernault 73a0f528ab
Update user domain block commands 2023-12-21 05:00:35 -07:00
Daniel Supernault d8f46f47a1
Update changelog 2023-12-21 04:48:56 -07:00
Daniel Supernault fa0380ac3b
Update UserObserver, add default domain blocks logic 2023-12-21 04:47:09 -07:00
Daniel Supernault 519c7a3735
Update domain block commands 2023-12-21 03:48:08 -07:00
Daniel Supernault f3f0175c84
Add DefaultDomainBlock model + migration 2023-12-21 03:47:23 -07:00
Daniel Supernault 3e28cf661b
Add user domain block commands 2023-12-21 03:35:47 -07:00
Daniel Supernault e98df1196f
Add migration 2023-12-21 03:34:31 -07:00
Daniel Supernault 6c39df7fb3
Update Inbox, import AccountService 2023-12-21 02:08:44 -07:00
Daniel Supernault 5169936062
Update MarkerService, fix php deprecation warning 2023-12-21 02:05:26 -07:00
Daniel Supernault 89b8e87477
Update ApiV1Controller, apply user domain blocks filtering to hashtag timelines 2023-12-21 02:03:15 -07:00
Daniel Supernault fcbcd7ec73
Update Delete pipelines, delete status hashtags quietly 2023-12-21 01:53:49 -07:00
Daniel Supernault c3f16c87a3
Update SearchApiV2Service, add user domain blocks filtering 2023-12-21 01:05:49 -07:00
Daniel Supernault 21947835f8
Update ApiV1Controller, use domainBlock filtering on public/network feeds 2023-12-21 00:46:24 -07:00
Daniel Supernault 6d81214138
Update DomainBlockController, purge domainBlocks cache 2023-12-21 00:44:54 -07:00
Daniel Supernault 6d55cb27ee
Update UserFilterService, add domainBlocks method 2023-12-21 00:42:26 -07:00
Daniel Supernault b3148b788e
Update HomeTimelineService, add domain blocks filtering to warmCache method 2023-12-21 00:21:33 -07:00
Daniel Supernault 29aa87c282
Update HomeFeedPipeline jobs, add domain block filtering 2023-12-21 00:17:20 -07:00
Daniel Supernault 0455dd1996
Update UserFilter model, add user relation 2023-12-20 23:55:26 -07:00
Daniel Supernault ae1db1e3ab
Update migration 2023-12-20 23:17:27 -07:00
Daniel Supernault dd16189fc8
Update ImageResize job, add more logging 2023-12-20 23:10:57 -07:00
Daniel Supernault 795132df18
Update changelog 2023-12-19 06:25:39 -07:00
Daniel Supernault 87bba03d23
Update DomainBlockController, dispatch jobies 2023-12-19 06:24:51 -07:00
Daniel Supernault 54adbeb059
Update FeedRemoveDomainPipeline, make batchable 2023-12-19 06:04:03 -07:00
Daniel Supernault 9d621108b0
Add ProfilePurgeNotificationsByDomain pipeline job 2023-12-19 06:03:36 -07:00
Daniel Supernault 484a377a44
Add ProfilePurgeFollowersByDomain pipeline job 2023-12-19 06:02:58 -07:00
Daniel Supernault 1664a5bc52
Update FollowerService, add $silent param to remove method to more efficently purge relationships 2023-12-19 05:46:06 -07:00
Daniel Supernault a492a95a0e
Update AdminShadowFilter, fix deleted profile bug 2023-12-19 04:01:41 -07:00
Daniel Supernault 5c1591fdff
Add job batches migration 2023-12-19 01:20:14 -07:00
Daniel Supernault 819e7d3b32
Add FeedRemoveDomainPipeline 2023-12-19 01:10:48 -07:00
Daniel Supernault 8a0ceaf801
Update Inbox, add user domain blocks to Story reaction handlers 2023-12-18 22:57:53 -07:00
Daniel Supernault 491468612f
Update Inbox, add user domain blocks to Undo handler 2023-12-18 22:49:31 -07:00
Daniel Supernault e32e50da7b
Update Inbox, add user domain blocks to Like handler 2023-12-18 22:47:03 -07:00
Daniel Supernault 3fbf8f159e
Update Inbox, add user domain blocks to Accept handler 2023-12-18 22:46:09 -07:00
Daniel Supernault 279fb28e2a
Update Inbox, add user domain blocks to Announce handler 2023-12-18 22:42:21 -07:00
Daniel Supernault c89dc45e8d
Update Inbox, add user domain blocks to Follow handler 2023-12-18 22:37:34 -07:00
Daniel Supernault a7f96d8194
Update Inbox, add user domain blocks to Direct Message handler 2023-12-18 22:34:53 -07:00
Daniel Supernault e7c08fbbb2
Update AccountService, add blocksDomain method 2023-12-18 22:32:48 -07:00
Daniel Supernault 7016d19520
Update Privacy Settings view, change button to Blocked Domains and add l10n 2023-12-16 17:04:16 -07:00
Daniel Supernault 60e053c936
Update ApiV1Controller, update discoverAccountsPopular method 2023-12-16 06:22:56 -07:00
Daniel Supernault d3f032b2ec
Update FollowerService, add quickCheck to follows method for non cold-boot checks 2023-12-16 06:11:13 -07:00
Daniel Supernault e5d789e0ab
Add domain blocks setting view 2023-12-16 06:01:43 -07:00
Daniel Supernault 28da107f66
Add DomainBlockController 2023-12-16 05:56:37 -07:00
Daniel Supernault 63c9ebe81f
Update api routes 2023-12-16 05:43:37 -07:00
Daniel Supernault 28da44beec
Update PrivacySettings, add domainBlocks 2023-12-16 05:42:56 -07:00
Daniel Supernault cef451e588
Update routes 2023-12-16 05:36:59 -07:00
Daniel Supernault 2438324369
Add template-vue settings blade view 2023-12-16 05:32:21 -07:00
Daniel Supernault 2136ffe3d8
Add localization 2023-12-16 05:30:52 -07:00
Daniel Supernault 5cea5aab3c
Add Domain Blocks 2023-12-16 04:56:22 -07:00
daniel c2a535bfa1
Merge pull request #4828 from pixelfed/staging
Staging
2023-12-13 06:50:58 -07:00
Daniel Supernault f22a36fe30
Update CHANGELOG 2023-12-13 06:49:51 -07:00
Daniel Supernault b641954549
Update ApiV1Controller, set last_active_at 2023-12-13 06:49:00 -07:00
Daniel Supernault ebbd98e743
Update AccountService, add setLastActive method 2023-12-13 06:30:05 -07:00
daniel 5e6658de25
Merge pull request #4827 from pixelfed/staging
Update cache/session config
2023-12-13 06:08:00 -07:00
Daniel Supernault 85839b220a
Update cache/session config 2023-12-13 04:46:49 -07:00
daniel 11eef54b0c
Merge pull request #4826 from pixelfed/staging
Allow Import from IG media to be stored on S3
2023-12-12 23:19:34 -07:00
Daniel Supernault ff92015c87
Add migration 2023-12-12 23:07:25 -07:00
daniel a5a2f77871
Merge pull request #4822 from pixelfed/staging
Update Inbox, improve tombstone query efficiency
2023-12-11 04:13:52 -07:00
Daniel Supernault 8d98e3dc97
Update changelog 2023-12-11 04:10:13 -07:00
Daniel Supernault 759a439334
Update Inbox, improve tombstone query efficiency 2023-12-11 04:09:33 -07:00
daniel 1c40762921
Merge pull request #4820 from pixelfed/staging
Add Mutual Followers API endpoint
2023-12-11 01:43:36 -07:00
Daniel Supernault 6dceb6f05b
Update changelog 2023-12-11 01:37:11 -07:00
Daniel Supernault 33dbbe467d
Add Mutual Followers API endpoint 2023-12-11 01:34:46 -07:00
daniel 7ac1e398b8
Merge pull request #4817 from pixelfed/staging
Update HomeFeedPipeline, fix StatusService validation
2023-12-09 23:53:58 -07:00
Daniel Supernault 041c01359b
Update HomeFeedPipeline, fix StatusService validation 2023-12-09 23:53:02 -07:00
daniel d66cf5d028
Merge pull request #4806 from pixelfed/staging
Update DirectMessageController
2023-12-08 05:48:38 -07:00
Daniel Supernault 38fee418a9
Update DirectMessageController 2023-12-08 05:48:04 -07:00
daniel abcaa19ff1
Merge pull request #4805 from pixelfed/staging
Staging
2023-12-08 05:27:07 -07:00
Daniel Supernault ed5e956a54
Update changelog 2023-12-08 05:25:44 -07:00
Daniel Supernault 9c43e7e265
Update Timeline.vue, improve CHT pagination 2023-12-08 05:25:03 -07:00
Daniel Supernault 822e9888bb
Update PhotoAlbumPresenter.vue, fix fullscreen mode 2023-12-08 05:01:04 -07:00
Daniel Supernault 0a0681199f
Update ComposeModal, fix missing alttext post state 2023-12-08 04:56:21 -07:00
Daniel Supernault 4c3823b0c4
Update Notifications.vue, fix deprecated DM action links for story activities 2023-12-08 04:51:29 -07:00
Daniel Supernault 4c95306f12
Update StatusPipeline, fix Direct and Story notification deletion 2023-12-08 04:44:45 -07:00
Daniel Supernault 9818656425
Update DirectMessageController, dispatch local deletes to pipeline 2023-12-08 04:27:09 -07:00
daniel f01f4bf23e
Merge pull request #4804 from pixelfed/staging
Staging
2023-12-08 03:27:52 -07:00
Daniel Supernault 93a6f1e224
formatting 2023-12-08 03:26:32 -07:00
Daniel Supernault 957bbbc2bd
Update FeedInsertPipeline 2023-12-08 03:25:53 -07:00
Daniel Supernault 06bee36c52
Update Inbox, improve story attribute collection 2023-12-08 03:24:09 -07:00
daniel 4bb97e1547
Merge pull request #4803 from pixelfed/staging
Update DirectMessageController, revert delete delivery to sharedInbox
2023-12-08 02:41:37 -07:00
Daniel Supernault d1c297d1ad
Update DirectMessageController, revert delete delivery to sharedInbox 2023-12-08 02:36:56 -07:00
daniel 128415dbf8
Merge pull request #4802 from pixelfed/staging
Staging
2023-12-08 02:09:41 -07:00
Daniel Supernault 7f462a8055
Update DirectMessageController, dispatch deliver and delete actions to the job queue 2023-12-08 02:07:26 -07:00
Daniel Supernault d848792ad4
Update DirectMessageController, deliver direct delete activities to user inbox instead of sharedInbox 2023-12-08 01:38:17 -07:00
daniel a4030faa9d
Merge pull request #4801 from pixelfed/staging
Update Inbox handler, fix missing object_url and uri fields for direc…
2023-12-08 01:16:50 -07:00
Daniel Supernault 4cc66a838d
Update changelog 2023-12-08 01:15:41 -07:00
Daniel Supernault a0157fce0c
Update Inbox handler, fix missing object_url and uri fields for direct statuses 2023-12-08 01:13:04 -07:00
daniel b74e813cba
Merge pull request #4796 from pixelfed/staging
Update FederationController, add proper statuses counts
2023-12-05 00:56:12 -07:00
Daniel Supernault dec061f5ae
Update FederationController, add proper statuses counts 2023-12-05 00:55:41 -07:00
daniel f9badbf4dd
Merge pull request #4795 from pixelfed/staging
Staging
2023-12-05 00:50:51 -07:00
Daniel Supernault 3204fb9669
Update FederationController, add proper following/follower counts 2023-12-05 00:48:14 -07:00
daniel 6ffc964371
Merge pull request #4792 from mbliznikova/4790_4791_add_recently_deleted_post_to_collection_no_page_reloading_invalidate_cache_after_adding
4790 4791 add recently deleted post to collection no page reloading invalidate cache after adding
2023-12-05 00:28:42 -07:00
daniel baa653d7de
Merge pull request #4750 from mbliznikova/3698_make_unlisted_photos_visible_in_collections
3698 make unlisted photos visible in collections
2023-12-05 00:27:30 -07:00
daniel cdd153d385
Merge pull request #4591 from Happyfeet01/dev
Updating Libwebp6 to libwebp7
2023-12-05 00:23:46 -07:00
daniel c2ce63ecd3
Merge branch 'staging' into dev 2023-12-05 00:23:26 -07:00
daniel d83df5cd64
Merge pull request #4794 from pixelfed/staging
Add WebPush
2023-12-05 00:18:19 -07:00
Daniel Supernault 4a1363b929
Add WebPush 2023-12-03 22:18:38 -07:00
daniel 7a6ef5fcbc
Merge pull request #4787 from pixelfed/staging
 Enhanced Places/Location tagging
2023-12-03 03:09:40 -07:00
Daniel Supernault fadb4d6ea4
Update changelog 2023-12-03 03:07:58 -07:00
Daniel Supernault 8548294c7a
Update HomeFeedPipeline, observe mutes/blocks during fanout 2023-12-03 03:06:32 -07:00
Daniel Supernault fe9b4c5a37
Update FollowServiceWarmCache 2023-12-03 03:05:00 -07:00
mbliznikova 7cb075dbf9 #4790 User experience: add a post to a collection just right after deleting it from there 2023-11-30 00:20:08 +00:00
mbliznikova a7320535e9 #4791 Invalidate cache after adding a collection item for data consistency 2023-11-30 00:19:04 +00:00
Daniel Supernault 1ef885c1a1
Add migration to add state and other fields to places table 2023-11-26 04:30:48 -07:00
daniel 66dc955d11
Merge pull request #4783 from pixelfed/staging
Update StoryApiV1Controller, add self-carousel endpoint. Fixes #4352
2023-11-18 01:14:07 -07:00
Daniel Supernault b0e8810a91
Update changelog 2023-11-18 01:13:55 -07:00
Daniel Supernault bcb88d5b0a
Update StoryApiV1Controller, add self-carousel endpoint. Fixes #4352 2023-11-18 01:11:12 -07:00
daniel 6eb256860c
Merge pull request #4782 from pixelfed/staging
Update app:hashtag-related-generate command, add existing confirmation
2023-11-17 22:45:26 -07:00
Daniel Supernault e5e3be0598
Update app:hashtag-related-generate command, add existing confirmation 2023-11-17 22:45:04 -07:00
daniel 54b6c96112
Merge pull request #4777 from pixelfed/staging
Add Related Hashtags
2023-11-17 22:25:06 -07:00
Daniel Supernault d62a60a4ee
Update changelog 2023-11-17 22:22:49 -07:00
Daniel Supernault 176b4ed793
Add app:hashtag-related-generate command 2023-11-17 22:21:55 -07:00
Daniel Supernault aa166ab11a
Update ApiV1Controller, move tags endpoints to TagsController 2023-11-17 22:10:03 -07:00
Daniel Supernault 287f903bf3
Update ApiV1Controller, fix include_reblogs param on timelines/home endpoint, and improve limit pagination logic 2023-11-17 20:50:07 -07:00
Daniel Supernault 175203089b
Add Related Hashtags 2023-11-16 06:06:22 -07:00
daniel 47b0354e16
Merge pull request #4776 from pixelfed/staging
Update HashtagService, reduce cached_count cache ttl
2023-11-16 03:27:11 -07:00
Daniel Supernault 051eb962e1
Update changelog 2023-11-16 02:54:30 -07:00
Daniel Supernault 15f29f7d79
Update HashtagService, reduce cached_count cache ttl 2023-11-16 02:53:22 -07:00
daniel 0899f909d8
Merge pull request #4775 from pixelfed/staging
Add app:hashtag-cached-count-update command to update cached_count of…
2023-11-16 02:44:32 -07:00
Daniel Supernault 4aca04729b
Update changelog 2023-11-16 02:44:21 -07:00
Daniel Supernault 1e31fee6a6
Add app:hashtag-cached-count-update command to update cached_count of hashtags and add to scheduler to run every 25 minutes past the hour 2023-11-16 02:43:11 -07:00
daniel 7f6d64b517
Merge pull request #4774 from pixelfed/staging
Staging
2023-11-16 00:50:09 -07:00
Daniel Supernault e6d3c7f4d7
Update changelog 2023-11-16 00:49:51 -07:00
Daniel Supernault f105f4e8f6
Update HomeFeedPipeline, fix tag filtering 2023-11-16 00:47:49 -07:00
Daniel Supernault e5401f8558
Update StatusHashtagService, remove problemaatic cache layer 2023-11-16 00:29:10 -07:00
daniel dfbc453b03
Merge pull request #4773 from pixelfed/staging
Update AP helpers, fix fanout scope
2023-11-15 23:44:23 -07:00
Daniel Supernault 33a60e767d
Update AP helpers, fix fanout scope 2023-11-15 23:43:57 -07:00
daniel 23dea20024
Merge pull request #4772 from pixelfed/staging
Update HashtagService, improve count perf
2023-11-15 23:24:46 -07:00
Daniel Supernault e1b39bcf6f
Update changelog 2023-11-15 23:24:32 -07:00
Daniel Supernault 3327a008fa
Update HashtagService, improve count perf 2023-11-15 23:23:17 -07:00
daniel 8b8fb5f6c0
Merge pull request #4771 from pixelfed/staging
Updaet HashtagUnfollowPipeline, fix typo
2023-11-15 22:37:46 -07:00
Daniel Supernault 3e96fa8a56
Updaet HashtagUnfollowPipeline, fix typo 2023-11-15 22:37:21 -07:00
daniel 0d48cf1c2e
Merge pull request #4770 from pixelfed/staging
Update Experimental Home Feed, fix remote posts, shares and reblogs
2023-11-15 22:17:15 -07:00
Daniel Supernault 19233cc976
Update HashtagFollowObserver 2023-11-15 22:16:23 -07:00
Daniel Supernault c6a6b3ae30
Update Experimental Home Feed, fix remote posts, shares and reblogs 2023-11-15 21:57:13 -07:00
daniel 57584391a4
Merge pull request #4769 from pixelfed/staging
Update ApiV1Controller
2023-11-15 00:18:02 -07:00
Daniel Supernault b365aa7e06
Update ApiV1Controller 2023-11-15 00:17:31 -07:00
daniel 40651c036a
Merge pull request #4768 from pixelfed/staging
Update HashtagUnfollowPipeline
2023-11-15 00:03:01 -07:00
Daniel Supernault c8092116e5
Update HashtagUnfollowPipeline 2023-11-15 00:02:17 -07:00
daniel c5cb2c0a1c
Merge pull request #4767 from pixelfed/staging
Update HashtagFollowService, fix cache invalidation bug
2023-11-14 23:56:58 -07:00
Daniel Supernault 84f4e88573
Update HashtagFollowService, fix cache invalidation bug 2023-11-14 23:55:53 -07:00
daniel f203d0540f
Merge pull request #4766 from pixelfed/staging
Update HashtagUnfollowPipeline
2023-11-14 23:06:49 -07:00
Daniel Supernault d8fbb4ff32
Update HashtagUnfollowPipeline 2023-11-14 23:05:17 -07:00
daniel 4071a4687d
Merge pull request #4759 from pixelfed/staging
Update IncrementPostCount job
2023-11-13 06:34:27 -07:00
Daniel Supernault cf50618696
Update FeedInsertPipeline, self fanout, oof 2023-11-13 06:33:52 -07:00
Daniel Supernault a5204f3e67
Update changelog 2023-11-13 06:12:30 -07:00
Daniel Supernault b2c9cc2318
Update IncrementPostCount job 2023-11-13 06:11:39 -07:00
daniel cac7c6bf3c
Merge pull request #4758 from pixelfed/staging
Update hashtag following
2023-11-13 05:32:47 -07:00
Daniel Supernault dde858bd5f
Update changelog 2023-11-13 05:32:36 -07:00
Daniel Supernault 015b1b80b4
Update hashtag following 2023-11-13 05:29:38 -07:00
daniel 2e2a200659
Merge pull request #4757 from pixelfed/staging
Update notification epoch generation
2023-11-13 02:01:07 -07:00
Daniel Supernault 446ca3a878
Update notification epoch generation 2023-11-13 01:59:38 -07:00
daniel 0a2a3b996d
Merge pull request #4756 from pixelfed/staging
Update mail config
2023-11-13 01:02:11 -07:00
Daniel Supernault 06bf0c14bf
Update changelog 2023-11-13 01:01:55 -07:00
Daniel Supernault 0e43127197
Update mail config 2023-11-13 01:00:53 -07:00
daniel e8439358cb
Merge pull request #4752 from pixelfed/staging
Experimental home feed
2023-11-13 00:50:26 -07:00
Daniel Supernault 05d646c034
Update changelog 2023-11-13 00:00:53 -07:00
Daniel Supernault c39b9afbfd
Update HomeTimelineService, apply filters to feed warm logic 2023-11-12 23:53:22 -07:00
Daniel Supernault 386e64d5e8
Update StatusEntityLexer, skip reblogs on FeedInsertPipeline 2023-11-12 23:52:44 -07:00
Daniel Supernault 125208fb9e
Update UserFilterObserver, dispatch FeedFollowPipeline jobs 2023-11-12 23:52:10 -07:00
Daniel Supernault e917341651
Update ApiV1Controller 2023-11-12 23:32:45 -07:00
Daniel Supernault 7deaaed4dd
Add migration 2023-11-12 23:32:14 -07:00
Daniel Supernault 43443503a1
Update FeedFollowPipeline, use more efficient query 2023-11-12 23:00:33 -07:00
Daniel Supernault 115a9d2dec
Update HomeTimelineService 2023-11-12 22:45:09 -07:00
Daniel Supernault 73cb8b43b3
Update HomeFeedPipeline, add follow/unfollow 2023-11-12 22:44:15 -07:00
Daniel Supernault 24c370ee22
Update ApiV1Controller, add experimental home timeline support to v1/timelines/home 2023-11-12 21:13:08 -07:00
Daniel Supernault 2a8a299058
Update HomeTimelineService 2023-11-12 21:09:06 -07:00
Daniel Supernault ce63c4997b
Add Feed fanout 2023-11-12 20:54:32 -07:00
Daniel Supernault de2b5ba4e9
Update FollowerService, reduce localFollowerIds ttl 2023-11-12 16:36:02 -07:00
Daniel Supernault df1f98d5f7
Add FeedInsertPipeline job 2023-11-11 07:23:42 -07:00
Daniel Supernault 20a560bfd1
Update FollowerService, add localFollowerIds method 2023-11-11 07:23:11 -07:00
Daniel Supernault 0fce5de6cd
Update composer deps 2023-11-11 05:53:16 -07:00
Daniel Supernault c806bbce3f
Update composer deps 2023-11-11 05:51:10 -07:00
Daniel Supernault 6aa65b9a21
Add FeedWarmCachePipeline 2023-11-11 05:49:41 -07:00
Daniel Supernault 1cd96ced2a
Update StatusHashtagObserver 2023-11-11 05:47:52 -07:00
Daniel Supernault 9dfc377322
Add HomeTimelineService 2023-11-11 05:46:37 -07:00
Daniel Supernault 448c061070
Update HomeFeedPipeline, add hashtag jobs 2023-11-11 05:43:33 -07:00
Daniel Supernault 1f35da0d4b
Update HashtagServices 2023-11-11 05:25:23 -07:00
Daniel Supernault ce54d29c69
Update delete pipelines, properly invoke StatusHashtag delete events 2023-11-11 03:40:59 -07:00
mbliznikova 2c6edf37a7 oFix #3698, make unlisted photos visible in collections 2023-11-11 04:08:29 +00:00
mbliznikova 170f877c26 Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-11-09 18:05:09 +00:00
daniel d20efd2c61
Merge pull request #4746 from pixelfed/staging
Update AP helpers, improve preferredUsername validation
2023-11-09 02:50:28 -07:00
Daniel Supernault d24c60576f
Update changelog 2023-11-09 02:50:09 -07:00
Daniel Supernault 21218c794b
Update AP helpers, improve preferredUsername validation 2023-11-09 02:47:20 -07:00
mbliznikova 439c8fc0ea Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-11-07 17:00:49 +00:00
daniel 1bdd0b3609
Merge pull request #4740 from pixelfed/staging
Update ApiV1Controller, fix mutes in home feed
2023-11-07 02:28:35 -07:00
Daniel Supernault ff272292ef
Update changelog 2023-11-07 02:27:16 -07:00
Daniel Supernault ddc217147c
Update ApiV1Controller, fix mutes in home feed 2023-11-07 02:24:52 -07:00
daniel c0575ae3bf
Merge pull request #4739 from pixelfed/staging
Add S3 command to rewrite media urls
2023-11-07 00:59:42 -07:00
Daniel Supernault d84c84c1e2
Update changelog 2023-11-07 00:54:01 -07:00
Daniel Supernault 5b3a56102f
Add S3 command to rewrite media urls 2023-11-07 00:49:36 -07:00
mbliznikova 3425821b55 Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-11-06 21:02:25 +00:00
daniel aaa0c7f76c
Merge pull request #4737 from pixelfed/staging
Update http client
2023-11-06 02:09:21 -07:00
Daniel Supernault c7b304ef20
Update http client 2023-11-06 02:08:51 -07:00
daniel 091fa1a62b
Merge pull request #4735 from pixelfed/staging
Update vue components, fix typos
2023-11-03 21:38:02 -06:00
Daniel Supernault a3fd0b032b
Update vue components, fix typos 2023-11-03 21:37:13 -06:00
daniel e45ede5a12
Merge pull request #4730 from pixelfed/staging
Staging
2023-11-02 04:37:39 -06:00
Daniel Supernault 960594f90d
Update changelog 2023-11-02 04:37:17 -06:00
Daniel Supernault c09a7d1127
Update compiled assets 2023-11-02 04:36:42 -06:00
Daniel Supernault 9c24157ab3
Update ImportPostController, fix IG bug with missing spaces between hashtags 2023-11-02 04:31:59 -06:00
Daniel Supernault 5a2d7e3eca
Update AccountImport 2023-11-02 04:31:15 -06:00
daniel e6301bfa51
Merge pull request #4726 from paulexyz/insta-import-utf8
fix: Instagram import broken UTF8 characters
2023-11-02 04:27:19 -06:00
mbliznikova 3269481148 Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-11-01 21:57:53 +00:00
paule 950baef58b fix: Instagram import broken UTF8 characters 2023-11-01 06:20:23 +01:00
daniel 4b9d0dc6ef
Merge pull request #4725 from pixelfed/staging
Update LikePipeline, dispatch to feed queue. Fixes #4723
2023-10-31 02:12:32 -06:00
Daniel Supernault da510089e2
Update LikePipeline, dispatch to feed queue. Fixes #4723 2023-10-30 21:16:28 -06:00
mbliznikova 770409c4a4 Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-10-30 20:47:18 +00:00
daniel 7960ab9222
Merge pull request #4721 from pixelfed/staging
Staging
2023-10-29 07:34:41 -06:00
Daniel Supernault eb291efe00
Update changelog 2023-10-29 05:29:56 -06:00
Daniel Supernault 4c6a0719ca
Update ApiV1Dot1Controller, add configurable app confirm rate limit ttl 2023-10-29 05:29:30 -06:00
Daniel Supernault 1686fc68e8
Update pixelfed config 2023-10-29 05:28:13 -06:00
Daniel Supernault 7cd9fa6e5b
Update pixelfed config 2023-10-29 05:27:12 -06:00
daniel 12fc8fd0c0
Merge pull request #4720 from pixelfed/staging
Update ApiV1Dot1Controller, update iar redirect url format
2023-10-29 04:50:58 -06:00
Daniel Supernault 3249695066
Update ApiV1Dot1Controller, update iar redirect url format 2023-10-29 04:50:20 -06:00
daniel e0208a7dd9
Merge pull request #4719 from pixelfed/staging
Update ApiV1Dot1Controller, update iar redirect url format
2023-10-29 04:07:59 -06:00
Daniel Supernault 432acb491a
Update ApiV1Dot1Controller, update iar redirect url format 2023-10-29 04:07:04 -06:00
daniel 81db60df32
Merge pull request #4718 from pixelfed/staging
Update ApiV1Dot1Controller, add domain to iar redirect
2023-10-29 03:49:47 -06:00
Daniel Supernault 1f82d47ce5
Update ApiV1Dot1Controller, add domain to iar redirect 2023-10-29 03:47:06 -06:00
daniel 4bac21d5d5
Merge pull request #4717 from pixelfed/staging
Staging
2023-10-29 03:08:19 -06:00
Daniel Supernault 28a808031b
Update ApiV1Dot1Controller, allow iar rate limits to be configurable 2023-10-29 03:06:10 -06:00
Daniel Supernault b58ed0ad01
Update pixelfed config 2023-10-29 03:04:48 -06:00
mbliznikova a8f78aa2ab Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-10-25 18:10:47 +00:00
daniel 2b17cc2c0d
Merge pull request #4709 from mbliznikova/check_if_collection_empty_in_edit
Add check if collection is empty in Edit Collection before publishing
2023-10-24 20:00:09 -06:00
daniel 1be012e439
Merge pull request #4698 from aneillans/strip-profile-tags-embed
Strip tags from bio in embeds
2023-10-24 19:59:28 -06:00
daniel 381e23e172
Merge pull request #4684 from mbliznikova/3596_provide_error_message_for_too_large_files
Provide the error message if a file to upload is too large, issue #3596
2023-10-24 19:56:28 -06:00
daniel d85c0c3d0a
Merge pull request #4676 from viviicat/fix-post-follow
Update Post component, adding follow and unfollow methods.
2023-10-24 19:54:28 -06:00
mbliznikova e3de4c3e68 Merge branch 'staging' of github.com:mbliznikova/pixelfed into staging 2023-10-23 18:46:11 +00:00
daniel 42fb713092
Merge pull request #4713 from pixelfed/staging
Add WebP2P support for Video
2023-10-23 03:38:32 -06:00
Daniel Supernault 31fafb1b68
Update changelog 2023-10-23 02:47:29 -06:00
Daniel Supernault 7edfea0951
Update hls pipeline, improve version check 2023-10-23 02:46:56 -06:00
Daniel Supernault 6ab7e37a48
Update ffmpeg config 2023-10-23 02:31:54 -06:00
Daniel Supernault 0405ef1248
Update compiled assets 2023-10-23 01:58:16 -06:00
Daniel Supernault c63707b3ec
Update npm deps 2023-10-23 01:35:19 -06:00
Daniel Supernault f11ce7009f
Update PostContent, add new video-player component 2023-10-23 01:28:06 -06:00
Daniel Supernault e3f8cfb49e
Add hls/p2p video player 2023-10-23 01:27:11 -06:00
Daniel Supernault 5c358010b0
Update Config util, add hls attributes 2023-10-23 01:15:02 -06:00
Daniel Supernault 6cf4363c50
Update MediaService, remove hls_manifest attribute for MastoAPI entities 2023-10-23 01:14:33 -06:00
Daniel Supernault f0ba2dfc69
Update VideoThumbnail job, dispatch HLS job when applicable 2023-10-23 01:13:09 -06:00
Daniel Supernault 3f292459ff
Update VideoPipeline, add VideoHlsPipeline job for HLS generation 2023-10-23 01:12:40 -06:00
Daniel Supernault f9bbb05575
Update MediaDeletePipeline, handle HLS deletion 2023-10-23 01:09:16 -06:00
Daniel Supernault fac7c3c5e7
Update MediaTransformer, add hls_manifest attribute 2023-10-23 00:38:37 -06:00
Daniel Supernault 4e3e23db36
Add js debounce util 2023-10-23 00:15:53 -06:00
Daniel Supernault a144301085
Add RegisterForm component 2023-10-23 00:11:46 -06:00
Daniel Supernault 4cd53247a6
Add MediaHlsService 2023-10-22 23:42:25 -06:00
Daniel Supernault 82fc36b2b3
Update npm deps, add webp2p libs. Thanks @peertube <3 2023-10-22 23:41:03 -06:00
Daniel Supernault 00823545a5
Add WebP2P support for Video 2023-10-22 23:21:50 -06:00
daniel b4a918ef42
Merge pull request #4712 from pixelfed/staging
Update ComposeModal component, fix multi filter bug and allow media r…
2023-10-22 21:15:12 -06:00
Daniel Supernault 56e315f69f
Update ComposeModal component, fix multi filter bug and allow media re-ordering before upload/posting 2023-10-22 21:13:55 -06:00
Happyfeet01 2a0ef7620d
Merge branch 'pixelfed:dev' into dev 2023-10-22 21:03:36 +02:00
mbliznikova b838f90b77 Add check if collection is empty in Edit Collection before publishing 2023-10-20 21:16:19 +00:00
mbliznikova fdb51d1f5a Add check if collection is empty before publishing 2023-10-20 21:09:29 +00:00
daniel 352786144b
Merge pull request #4707 from pixelfed/staging
Update StatusTransformer
2023-10-20 00:18:21 -06:00
Daniel Supernault 65a048cdd5
Update StatusTransformer 2023-10-20 00:17:24 -06:00
daniel 7cbdac7adb
Merge pull request #4702 from pixelfed/staging
Update StatusTransformer, generate autolink on request
2023-10-16 06:30:16 -06:00
Daniel Supernault dfe2379b93
Update StatusTransformer, generate autolink on request 2023-10-16 06:28:57 -06:00
daniel 9f968d134e
Merge pull request #4700 from pixelfed/staging
Staging
2023-10-15 03:53:14 -06:00
Daniel Supernault 9677791bef
Update changelog 2023-10-15 03:52:20 -06:00
Daniel Supernault 778e83d398
Update lexer regex, fix mention regex and add more tests 2023-10-15 03:51:45 -06:00
daniel a1b280ec33
Merge pull request #4699 from pixelfed/staging
Update nodeinfo
2023-10-12 21:30:30 -06:00
Daniel Supernault 36df0d8373
Update nodeinfo 2023-10-12 21:29:51 -06:00
Andy Neillans e9d9c4d8cc Strip tags from bio in embeds 2023-10-11 19:08:22 +01:00
daniel 2fa595d2cf
Merge pull request #4697 from pixelfed/staging
Update RemoteStatusDelete, fix include
2023-10-11 05:25:10 -06:00
Daniel Supernault b76ad7cfe0
Update RemoteStatusDelete, fix include 2023-10-11 05:24:34 -06:00
daniel c906dbb26b
Merge pull request #4696 from pixelfed/staging
Update IncrementPostCount pipeline
2023-10-11 05:12:32 -06:00
Daniel Supernault 0d35f1a3e5
Update IncrementPostCount pipeline 2023-10-11 05:10:32 -06:00
daniel 627be42b36
Merge pull request #4695 from pixelfed/staging
Staging
2023-10-11 05:06:24 -06:00
Daniel Supernault f481f3d248
Update RemoteStatusDelete pipeline 2023-10-11 04:56:39 -06:00
Daniel Supernault edbcf3ed79
Update RemoteStatusDelete and DecrementPostCount pipelines 2023-10-11 04:42:40 -06:00
daniel 8f4f64d737
Merge pull request #4694 from pixelfed/staging
Update AvatarPipeline, improve refresh logic and garbage collection
2023-10-11 03:42:21 -06:00
Daniel Supernault 4e35f0d32e
Update changelog 2023-10-11 03:39:35 -06:00
Daniel Supernault c37b7cde30
Add `avatar:storage-deep-clean` command to dispatch avatar storage cleanup jobs 2023-10-11 03:38:31 -06:00
Daniel Supernault 319ced4054
Update CreateAvatar job, add processing constraints and set is_remote attribute 2023-10-11 01:32:04 -06:00
Daniel Supernault 95a1eddcb2
Update changelog 2023-10-11 00:48:56 -06:00
Daniel Supernault 82798b5ea3
Update AvatarPipeline, improve refresh logic and garbage collection to purge old avatars 2023-10-11 00:48:30 -06:00
daniel ffa44c4fad
Merge pull request #4693 from pixelfed/staging
Update AP helpers, adjust RemoteAvatarFetch ttl from 24h to 3 months
2023-10-10 21:28:02 -06:00
Daniel Supernault 36b23fe34e
Update AP helpers, adjust RemoteAvatarFetch ttl from 24h to 3 months 2023-10-10 20:55:20 -06:00
daniel 6f314fa0d2
Merge pull request #4692 from pixelfed/staging
Update user:admin command, improve logic. Fixes #2465
2023-10-10 20:23:16 -06:00
Daniel Supernault 01bac51104
Update user:admin command, improve logic. Fixes #2465 2023-10-10 20:20:18 -06:00
daniel dadb6ab416
Merge pull request #4691 from pixelfed/staging
Staging
2023-10-10 20:08:56 -06:00
Daniel Supernault 7bfe43095b
Update changelog 2023-10-10 20:08:16 -06:00
Daniel Supernault c6408fd79d
Add user:2fa command to easily disable 2FA for given account 2023-10-10 20:00:12 -06:00
daniel a276b2ce70
Merge pull request #4690 from pixelfed/staging
Update NotificationService, handle empty epoch. Fixes #4689
2023-10-10 19:14:12 -06:00
Daniel Supernault 457d5454f8
Update NotificationService, handle empty epoch. Fixes #4689 2023-10-10 19:13:19 -06:00
daniel f38226c527
Merge pull request #4688 from pixelfed/staging
Update AdminReportController, add `profile_id` to group by. Fixes #4685
2023-10-09 14:59:59 -06:00
Daniel Supernault e4d3b19642
Update AdminReportController, add `profile_id` to group by. Fixes #4685 2023-10-09 13:44:25 -06:00
daniel d679ae4f11
Merge pull request #4687 from pixelfed/staging
Update ApiV1Controller, hydrate reblog interactions. Fixes #4686
2023-10-09 13:08:27 -06:00
Daniel Supernault 135798eb68
Update ApiV1Controller, hydrate reblog interactions. Fixes #4686 2023-10-09 13:06:46 -06:00
mbliznikova 6c1e56fcb2 Provide the error message if a file to upload is too large 2023-10-06 02:05:22 +00:00
daniel ed20344c3e
Merge pull request #4682 from pixelfed/staging
Update profile embeds, filter sensitive posts
2023-09-30 14:49:12 -06:00
Daniel Supernault ede5ec3bf4
Update profile embeds, filter sensitive posts 2023-09-30 14:45:24 -06:00
Vivianne Langdon 4508697563 Update Post component, adding follow and unfollow methods. 2023-09-28 00:20:09 -07:00
daniel eb517aa8bf
Merge pull request #4674 from pixelfed/staging
Update Sign-in with Mastodon, allow usage when registrations are closed
2023-09-27 01:38:23 -06:00
Daniel Supernault a1e162f095
Update changelog 2023-09-27 01:38:05 -06:00
Daniel Supernault 895dc4fa9e
Update Sign-in with Mastodon, allow usage when registrations are closed 2023-09-27 01:33:39 -06:00
daniel 705f30b865
Merge pull request #4672 from pixelfed/staging
Staging
2023-09-26 23:17:19 -06:00
Daniel Supernault fcb4933369
Update changelog 2023-09-26 23:14:42 -06:00
Daniel Supernault 8c96919119
Update ap helpers, store media attachment width and height if present 2023-09-26 23:14:19 -06:00
Daniel Supernault ce1afe2711
Update Note and CreateNote transformers, include attachment blurhash, width and height 2023-09-26 23:10:20 -06:00
daniel d5baf2627a
Merge pull request #4670 from pixelfed/staging
Update StatusTagsPipeline, fix object tags slug query
2023-09-25 05:20:38 -06:00
Daniel Supernault 79b378cdb1
Update StatusTagsPipeline, fix object tags slug query 2023-09-25 05:20:04 -06:00
daniel dcc6f65e33
Merge pull request #4669 from pixelfed/staging
Update StatusTagsPipeline, fix object tags slug query
2023-09-25 05:14:22 -06:00
Daniel Supernault 9989d6c66f
Update StatusTagsPipeline, fix object tags slug query 2023-09-25 05:13:09 -06:00
daniel 7fdb87ef9b
Merge pull request #4668 from pixelfed/staging
Staging
2023-09-25 04:33:03 -06:00
Daniel Supernault bf5b72f082
Update changelog 2023-09-25 04:23:28 -06:00
Daniel Supernault d295e6059b
Update StatusTagsPipeline, fix object tags and slug normalization 2023-09-25 04:23:04 -06:00
daniel b2195ca837
Merge pull request #4667 from pixelfed/staging
Update Status model, allow unlisted thumbnails
2023-09-25 02:11:33 -06:00
Daniel Supernault 1f0a45b7f4
Update Status model, allow unlisted thumbnails 2023-09-25 02:10:19 -06:00
daniel 7b5999496e
Merge pull request #4666 from pixelfed/staging
Staging
2023-09-25 02:00:12 -06:00
Daniel Supernault 2d428f43e8
Update changelog 2023-09-25 01:58:09 -06:00
Daniel Supernault d969a97360
Update Status model, improve thumb logic 2023-09-25 01:57:43 -06:00
daniel 8a89570b4a
Merge pull request #4665 from pixelfed/staging
Add Resilient Media Storage
2023-09-25 01:14:11 -06:00
Daniel Supernault 439638f7d7
Update changelog 2023-09-25 01:13:14 -06:00
Daniel Supernault fb1deb6e28
Add Resilient Media Storage 2023-09-25 00:59:24 -06:00
daniel b91d263237
Merge pull request #4662 from pixelfed/staging
Update profile embed, fix resize
2023-09-21 06:48:16 -06:00
Daniel Supernault dcdfb28dcd
Update changelog 2023-09-21 06:47:59 -06:00
Daniel Supernault dc23c21db0
Update profile embed, fix resize 2023-09-21 06:42:10 -06:00
daniel 5a9a159708
Merge pull request #4656 from pixelfed/staging
Update NotificationService, fix order bug
2023-09-18 00:25:28 -06:00
Daniel Supernault 0210f8aa2a
Update NotificationService, fix order bug 2023-09-18 00:25:01 -06:00
daniel 19015f18b0
Merge pull request #4655 from pixelfed/staging
Update StatusService, fix logic check
2023-09-18 00:17:58 -06:00
Daniel Supernault 61d235b797
Update StatusService, fix logic check 2023-09-18 00:17:22 -06:00
daniel 4112ab5f83
Merge pull request #4654 from pixelfed/staging
Update NotificationService, improve cache warming query
2023-09-18 00:00:46 -06:00
Daniel Supernault 5ab7f9958c
Update changelog 2023-09-17 23:57:52 -06:00
Daniel Supernault 223661ecb2
Update StatusService, hydrate accounts on request instead of caching them along with status objects 2023-09-17 23:54:31 -06:00
Daniel Supernault 2496386d9b
Update NotificationService, improve cache warming query 2023-09-17 23:51:42 -06:00
daniel 155e1704ff
Merge pull request #4649 from pixelfed/staging
Add AdminShadowFilter model/migration
2023-09-14 22:39:29 -06:00
Daniel Supernault 33ed7a8c91
Add AdminShadowFilter feature 2023-09-14 22:32:37 -06:00
Daniel Supernault a510c3e89c
Add AdminShadowFilter model/migration 2023-09-14 22:23:46 -06:00
daniel e36d7da841
Merge pull request #4644 from pixelfed/staging
Staging
2023-09-10 01:18:10 -06:00
Daniel Supernault 3979e33b57
Update changelog 2023-09-10 01:14:08 -06:00
Daniel Supernault 8fa2afe016
Remove unused resource 2023-09-10 01:13:45 -06:00
Daniel Supernault 941736ce6c
Update StoryApiV1Controller, add viewers route to view story viewers 2023-09-10 01:12:27 -06:00
daniel c4843e823e
Merge pull request #4642 from b2cc/bugfix-4518-unable-to-view-reports-in-admin
[Bugfix] #4518: SQL query that generates the report list in the admin view needs to include the 'id' field
2023-09-09 20:48:18 -06:00
daniel 6ec4077549
Merge pull request #4643 from ThisIsMissEm/fix/memory-leak-in-blurhash-calculation
Fix potential memory leak due to not calling imagedestroy on GdImage objects
2023-09-09 15:31:11 -06:00
Emelia Smith 74ad26fee6
Fix potential memory leak due to not calling imagedestroy on GdImage objects 2023-09-09 22:55:06 +02:00
David Gabriel 2e5c141724 Fix similar SQL error which triggers when mentioning people in new posts 2023-09-09 20:46:50 +02:00
David Gabriel 480394f3d8 [Bugfix] Fix for #4518: SQL query that generates the report list in the admin view needs to include the 'id' field 2023-09-09 19:50:45 +02:00
daniel e286f98762
Merge pull request #4624 from pixelfed/staging
Update ProfileTransformer, fix Mastodon indexable context
2023-08-27 21:43:38 -06:00
Daniel Supernault 83900a3b00
Update ProfileTransformer, fix Mastodon indexable context 2023-08-27 21:43:11 -06:00
daniel 5dc397ec1f
Merge pull request #4613 from pixelfed/staging
Update Profile AP transformer, fix context
2023-08-24 23:55:09 -06:00
Daniel Supernault 817b494703
Update Profile AP transformer, fix context 2023-08-24 23:54:08 -06:00
daniel 24db7d71cf
Merge pull request #4612 from pixelfed/staging
Add support for Mastodon indexable search flag
2023-08-24 23:41:14 -06:00
Daniel Supernault 23bc985b36
Update changelog 2023-08-24 23:37:11 -06:00
Daniel Supernault fbdcdd9dbc
Update AP Helpers, consume actor `indexable` attribute 2023-08-24 23:36:50 -06:00
Daniel Supernault fc24630eba
Update Privacy Settings, add support for Mastodon indexable search flag 2023-08-24 23:31:33 -06:00
daniel 28bca423d7
Merge pull request #4603 from pixelfed/staging
Prepare for groups
2023-08-23 03:57:22 -06:00
Daniel Supernault a3696dac95
Update changelog 2023-08-23 03:54:00 -06:00
Daniel Supernault 61a6d90403
Update FollowServiceWarmCache, improve handling larger following/follower lists 2023-08-23 03:45:51 -06:00
Daniel Supernault 93c7ad9779
Update groups migration 2023-08-23 02:45:41 -06:00
Daniel Supernault 347e4f59a3
Update FollowerService, add forget method to RelationshipService call to reduce load when mass purging 2023-08-23 02:39:16 -06:00
Daniel Supernault a04ba18113
Add Groups migrations 2023-08-22 00:25:15 -06:00
daniel 3cb50af8a3
Merge pull request #4601 from pixelfed/staging
v0.11.9 🚀
2023-08-21 22:45:48 -06:00
Daniel Supernault a7e4305043
Bump version to 0.11.9 2023-08-21 22:45:13 -06:00
Daniel Supernault ca746717cb
Update ApiV1Controller, add bookmarked to timeline entities 2023-08-21 22:43:30 -06:00
Happyfeet01 1ea65db70d
Update Dockerfile.fpm
update libwp6 to libwp7
2023-08-16 11:05:55 +02:00
Happyfeet01 a6a0333170
Update Dockerfile.apache
Update libwp6 to libwp7
2023-08-16 11:05:11 +02:00
daniel b6c3ac4b13
Merge pull request #4578 from pixelfed/staging
Add Account Migrations
2023-08-08 00:03:18 -06:00
Daniel Supernault 526807f01c
Update web routes 2023-08-08 00:02:10 -06:00
Daniel Supernault 781d3c0ec3
Update changelog 2023-08-08 00:00:39 -06:00
Daniel Supernault 47e5c07061
Add ProfileAliasController, hello account migrations! 2023-08-08 00:00:24 -06:00
Daniel Supernault dc7973de62
Update Profile model, add aliases relation 2023-08-07 23:59:29 -06:00
Daniel Supernault 9378c65396
Add AccountMigration ActivityPub support 2023-08-07 23:59:10 -06:00
Daniel Supernault eab16e7fd8
Add Help Center Documentation for Account Migration 2023-08-07 23:58:21 -06:00
Daniel Supernault 3103af2fe4
Add Account Migration setting views 2023-08-07 23:58:02 -06:00
Daniel Supernault 3c60362648
Add moved_to_profile_id migration 2023-08-07 23:56:01 -06:00
Daniel Supernault a9220e4e01
Add Account Migrations 2023-08-07 23:48:58 -06:00
daniel 211a9057fc
Merge pull request #4576 from pixelfed/staging
Update RemoteStatusPipeline, fix reply check
2023-08-06 21:52:35 -06:00
Daniel Supernault 618b67271a
Update RemoteStatusPipeline, fix reply check 2023-08-06 21:52:01 -06:00
daniel 268804856b
Merge pull request #4575 from pixelfed/staging
Staging
2023-08-06 21:31:47 -06:00
Daniel Supernault fab8f25e9b
Update RemoteStatusDelete pipeline 2023-08-06 15:31:37 -06:00
Daniel Supernault 4f19a58b2c
Add AdminProfileActionPipeline 2023-08-01 05:48:43 -06:00
Daniel Supernault 6161cf45aa
Add AdminProfile resource 2023-08-01 05:48:27 -06:00
Daniel Supernault 71e92261f4
Update RemoteStatusDelete pipeline 2023-08-01 05:07:58 -06:00
Daniel Supernault 45be6e10b8
Update RemoteStatusDelete pipeline 2023-08-01 04:40:39 -06:00
Daniel Supernault ed87ddb923
Update RemoteStatusDelete pipeline 2023-08-01 04:30:50 -06:00
Daniel Supernault 3d1b6516fe
Update ActivityPubFetchService, add validateUrl parameter to bypass url validation to fetch content from blocked instances 2023-08-01 03:16:08 -06:00
daniel 2a63ff1d40
Merge pull request #4570 from pixelfed/staging
Staging
2023-07-31 21:22:03 -06:00
Daniel Supernault f2dfe12ac3
Update changelog 2023-07-31 21:21:37 -06:00
Daniel Supernault 3e90f6cee5
Update compiled assets 2023-07-31 21:21:23 -06:00
Daniel Supernault 911504fa54
Update PostContent, add text cw warning 2023-07-31 21:15:50 -06:00
daniel 5cdf076527
Merge pull request #4566 from pixelfed/staging
Add Photo reblogs only setting
2023-07-30 06:35:18 -06:00
Daniel Supernault c527858ac4
Update changelog 2023-07-30 06:31:17 -06:00
Daniel Supernault 75bfd21104
Update compiled assets 2023-07-30 06:31:09 -06:00
Daniel Supernault e2705b9ae9
Update timeline settings, add photo reblogs only option 2023-07-30 06:29:52 -06:00
Daniel Supernault dccec7d5a9
Update SettingsController, add photo_reblogs_only setting 2023-07-30 06:29:12 -06:00
daniel 2cdf8917da
Merge pull request #4565 from pixelfed/staging
Staging
2023-07-30 06:03:24 -06:00
Daniel Supernault 74a6b169d3
Update changelog 2023-07-30 06:02:38 -06:00
Daniel Supernault f4d46d8148
Update compiled assets 2023-07-30 06:02:30 -06:00
Daniel Supernault 29de91e5d0
Update Timeline component, improve reblog support 2023-07-30 06:01:12 -06:00
Daniel Supernault ec2a1ed99c
Update SharePipeline 2023-07-30 05:50:39 -06:00
Daniel Supernault 5a19daabce
Update SharePipeline 2023-07-30 05:45:14 -06:00
Daniel Supernault 13bdaa2ed4
Update ApiV1Controller, hydrate reblog state in home timeline 2023-07-30 05:16:51 -06:00
daniel e559187411
Merge pull request #4563 from pixelfed/staging
Reblogs in feeds
2023-07-30 04:11:47 -06:00
Daniel Supernault 685d45a8df
Update changelog 2023-07-30 04:11:25 -06:00
Daniel Supernault b86d47bfec
Update compiled assets 2023-07-30 04:07:49 -06:00
Daniel Supernault 8efb4047b1
Update Timeline component 2023-07-30 04:04:49 -06:00
Daniel Supernault 3b885709b8
Update ApiV1Dot1Controller 2023-07-30 04:01:29 -06:00
Daniel Supernault f54cf0b2d7
Update timeline settings 2023-07-30 03:59:35 -06:00
Daniel Supernault 0eca48f1a4
Update SettingsController 2023-07-30 03:55:51 -06:00
Daniel Supernault 1c13b518be
Update StatusStatelessTransformer, allow unlisted reblogs 2023-07-30 02:25:49 -06:00
Daniel Supernault c469d47552
Update admin users view, fix website value. Closes #4557 2023-07-30 00:58:31 -06:00
daniel acadd1b473
Merge pull request #4562 from pixelfed/staging
Update ProfileController, allow albums in atom feed. Closes #4561. Fi…
2023-07-30 00:51:11 -06:00
Daniel Supernault 1c105a6ce3
Update ProfileController, allow albums in atom feed. Closes #4561. Fixes #4526 2023-07-30 00:49:23 -06:00
daniel 93b2dbab17
Merge pull request #4554 from pixelfed/staging
Staging
2023-07-22 03:51:46 -06:00
Daniel Supernault 6c36995083
Update changelog 2023-07-22 03:50:43 -06:00
Daniel Supernault 59b643789f
Update StatusService, reduce cache ttl from 7 days to 6 hours 2023-07-22 03:50:23 -06:00
daniel 8e7963c0c5
Merge pull request #4549 from pixelfed/staging
Add Health check endpoint at /api/service/health-check
2023-07-18 02:12:59 -06:00
Daniel Supernault ecc697a241
Update changelog 2023-07-18 02:12:10 -06:00
Daniel Supernault ff58f9707f
Add Health check endpoint at /api/service/health-check 2023-07-18 02:11:06 -06:00
daniel 1a3176c996
Merge pull request #4548 from pixelfed/staging
Staging
2023-07-18 00:12:43 -06:00
Daniel Supernault 3f22640644
Update changelog 2023-07-17 23:44:43 -06:00
Daniel Supernault 95fb893f95
Update compiled assets 2023-07-17 23:44:30 -06:00
Daniel Supernault acabf603f0
Update Remote Auth feature, fix custom domain bug and enforce banned domains 2023-07-17 23:43:19 -06:00
daniel 8911ace102
Merge pull request #4545 from pixelfed/staging
Sign-in with Mastodon
2023-07-16 23:43:36 -06:00
Daniel Supernault 780e78f21a
Update changelog 2023-07-16 23:00:08 -06:00
Daniel Supernault a3dd7c95df
Update compiled assets 2023-07-16 22:59:52 -06:00
Daniel Supernault 852dbd8d34
Add remote_auth.js 2023-07-16 22:58:21 -06:00
Daniel Supernault 9cfa89dab4
Update routes and add RemoteAuthController 2023-07-16 22:54:02 -06:00
Daniel Supernault 0b90d629d5
Update User, fix last_active_at guard 2023-07-16 18:42:43 -06:00
Daniel Supernault 45b9404ec1
Add Sign-in with Mastodon 2023-07-16 07:09:15 -06:00
daniel 45090a0e76
Merge pull request #4544 from pixelfed/staging
Update MediaStorageService, improve head header handling
2023-07-16 05:41:49 -06:00
Daniel Supernault ce02f05718
Update changelog 2023-07-16 04:29:35 -06:00
Daniel Supernault 0d802c313b
Update FanoutDeletePipeline, fix AP object 2023-07-16 04:27:14 -06:00
Daniel Supernault 7a431af93a
Update Media model 2023-07-16 04:24:58 -06:00
Daniel Supernault ff2c16fe74
Update admin user view, improve previews 2023-07-16 04:23:41 -06:00
Daniel Supernault 3590adbd87
Update MediaStorageService, improve head header handling 2023-07-16 04:17:23 -06:00
daniel 1a30e488f9
Merge pull request #4541 from pixelfed/staging
Update user model
2023-07-14 04:33:41 -06:00
Daniel Supernault 4b611be2d9
Update user model 2023-07-14 04:33:05 -06:00
daniel ef58c3b304
Merge pull request #4540 from pixelfed/staging
Staging
2023-07-14 04:30:00 -06:00
Daniel Supernault c07233a1c1
Update changelog 2023-07-14 04:29:43 -06:00
Daniel Supernault e0b48b2976
Update admin users blade view, show last_active_at and other info 2023-07-14 04:28:37 -06:00
Daniel Supernault 2bef3e415d
Update AP Helpers, improve url validation and add optional dns verification, disabled by default 2023-07-14 03:10:48 -06:00
daniel 57582b1b2e
Merge pull request #4539 from pixelfed/staging
Staging
2023-07-14 01:50:53 -06:00
Daniel Supernault a00a520bf3
Update composer deps 2023-07-14 01:50:43 -06:00
Daniel Supernault 84669ac614
Update changelog 2023-07-14 01:48:34 -06:00
Daniel Supernault ba7551d8a9
Update TransformImports command, increment status_count on profile model 2023-07-14 01:46:51 -06:00
Daniel Supernault 0b5157675f
Update FollowPipeline, improve follower/following count calculation 2023-07-14 01:38:36 -06:00
Daniel Supernault c61d0b915f
Update SearchApiV2Service, improve resolve query logic to better handle remote posts/profiles and local posts/profiles 2023-07-14 01:22:49 -06:00
Daniel Supernault 0704c7e05e
Update AP Helpers, preserve admin unlisted state before adding to NetworkTimelineService 2023-07-14 01:20:26 -06:00
Daniel Supernault 9fa6b3f7aa
Update Inbox, allow storing Create->Note activities without any local followers, disabled by default 2023-07-13 23:11:19 -06:00
Daniel Supernault 4b2c66f557
Update Services, use zpopmin on predis 2023-07-13 22:06:21 -06:00
Daniel Supernault 1cc6274ac0
Update rate limits, fixes #4537 2023-07-13 21:38:18 -06:00
Daniel Supernault 9233cd8f5b
Add migration 2023-07-11 02:27:08 -06:00
daniel cc561c0522
Merge pull request #4533 from pixelfed/staging
Staging
2023-07-11 00:09:56 -06:00
Daniel Supernault 947847898a
Update changelog 2023-07-10 23:41:40 -06:00
Daniel Supernault 37fd03428a
Update ApiV1Controller, add include_reblogs attribute to home timeline 2023-07-10 23:41:17 -06:00
Daniel Supernault c244d8b5c8
Update composer 2023-07-10 18:49:52 -06:00
daniel 1809cb217c
Merge pull request #4515 from pixelfed/staging
Staging
2023-07-01 22:39:29 -06:00
Daniel Supernault 4276d3f248
Update changelog 2023-07-01 22:38:58 -06:00
Daniel Supernault 73b35d3231
Update compiled assets 2023-07-01 22:38:53 -06:00
Daniel Supernault 625a76a51d
Update AccountImport, add select first 100 posts button 2023-07-01 22:38:16 -06:00
Daniel Supernault 2d959fb354
Update ComposeModal.vue, fix scroll issue and dont hide scrollbar 2023-07-01 22:36:22 -06:00
daniel 924ce5eea9
Merge pull request #4510 from pixelfed/staging
Update StatusRemoteUpdatePipeline, fix missing mime and size attribut…
2023-06-28 06:41:58 -06:00
Daniel Supernault 43b101dbfa
Add FetchMissingMediaMimeType command 2023-06-28 06:41:32 -06:00
Daniel Supernault 72844c0715
Update changelog 2023-06-28 06:29:33 -06:00
Daniel Supernault ea54413e95
Update StatusRemoteUpdatePipeline, fix missing mime and size attributes that cause empty media previews on our mobile app 2023-06-28 06:28:13 -06:00
daniel f041f5f60a
Merge pull request #4509 from pixelfed/staging
Staging
2023-06-28 05:03:56 -06:00
Daniel Supernault 60747bfb15
Update changelog 2023-06-28 05:03:06 -06:00
Daniel Supernault 04f4f8baf1
Update FixStatusCount, improve command and support remote count resync 2023-06-28 05:02:41 -06:00
daniel da90bf630a
Merge pull request #4505 from pixelfed/staging
Update IG Import commands, fix stalled import queue
2023-06-26 05:53:45 -06:00
Daniel Supernault 2f2e446c1f
Update ImportService 2023-06-26 05:39:16 -06:00
Daniel Supernault fe6123c820
Update ImportPostController 2023-06-26 05:38:29 -06:00
Daniel Supernault 6fd53a3001
Update changelog 2023-06-26 04:47:43 -06:00
Daniel Supernault 10dd348c28
Update ImportService, filter deleted posts from getImportedPosts endpoint 2023-06-26 04:46:57 -06:00
Daniel Supernault d6d60a8574
Update changelog 2023-06-26 04:34:12 -06:00
Daniel Supernault afe6948da8
Update console kernel, add import upload gc 2023-06-26 04:33:47 -06:00
Daniel Supernault b47e8f8e3e
Update changelog 2023-06-26 04:18:39 -06:00
Daniel Supernault 892907d5d1
Update TransformImports command, improve handling of imported posts that already exist or are from deleted accounts 2023-06-26 04:16:49 -06:00
Daniel Supernault b18f3fba8b
Update IG Import commands, fix stalled import queue 2023-06-26 03:21:38 -06:00
daniel 48cd829572
Merge pull request #4504 from pixelfed/staging
Update ActivityPubFetchService, fix authorized_fetch support
2023-06-25 23:11:20 -06:00
Daniel Supernault c64c4aa1cb
Update ActivityPubFetchService, fix authorized_fetch compatibility. Closes #1850, #2713, #2935 2023-06-25 23:10:36 -06:00
Daniel Supernault 63a7879c29
Update ActivityPubFetchService 2023-06-25 23:02:02 -06:00
Daniel Supernault b89c4f1cdc
Update ActivityPubFetchService, fix authorized_fetch support 2023-06-25 22:26:04 -06:00
daniel 4412a6b5dd
Merge pull request #4500 from pixelfed/staging
Staging
2023-06-22 06:48:23 -06:00
Daniel Supernault 763ce19a0a
Update AdminApiController, improve admin moderation tools 2023-06-22 05:43:42 -06:00
Daniel Supernault 71ad7d5d43
Update AdminUser resource 2023-06-22 01:12:53 -06:00
Daniel Supernault 4f850e54ad
Update AdminApiController, include more data for getUser method 2023-06-22 01:08:15 -06:00
daniel 8606040858
Merge pull request #4492 from pixelfed/staging
Staging
2023-06-20 05:12:16 -06:00
Daniel Supernault 52f9999fcc
wip 2023-06-20 05:11:00 -06:00
Daniel Supernault 5c5541fc01
Update AdminApiController 2023-06-20 05:09:15 -06:00
Daniel Supernault a6d10f0389
Update RegisterController 2023-06-20 05:03:49 -06:00
Daniel Supernault 89c3710d3c
Update AdminApiController, add instance stats endpoint 2023-06-20 00:34:02 -06:00
daniel 2dcfd68f01
Merge pull request #4468 from pixelfed/staging
Update ImportPostController, fix typo
2023-06-12 06:20:01 -06:00
Daniel Supernault ba58aaba36
Update ImportPostController, fix typo 2023-06-12 06:19:09 -06:00
daniel 29554d20b7
Merge pull request #4467 from pixelfed/staging
Update sidebar/settings
2023-06-12 06:09:33 -06:00
Daniel Supernault c394fb76c6
Update sidebar/settings 2023-06-12 06:08:25 -06:00
daniel d76ae33eb9
Merge pull request #4466 from pixelfed/staging
Import from Instagram
2023-06-12 05:56:27 -06:00
Daniel Supernault 6d0c5994da
Update TransformImports command, bump limit from 10 to 100 2023-06-12 05:53:29 -06:00
Daniel Supernault 477986abdd
Add import scheduler 2023-06-12 05:46:56 -06:00
Daniel Supernault aabc20dd2e
Update changelog 2023-06-12 05:32:42 -06:00
Daniel Supernault cf3078c569
Update compiled assets 2023-06-12 05:31:23 -06:00
Daniel Supernault 7de67650c7
Update import blade view 2023-06-12 05:30:18 -06:00
Daniel Supernault 5d8599a497
Update npm deps 2023-06-12 05:29:16 -06:00
Daniel Supernault 828f369373
Add AccountImport component 2023-06-12 05:26:24 -06:00
Daniel Supernault 9f3e809f26
Add Import Help Center page 2023-06-12 05:16:20 -06:00
Daniel Supernault 7e0335b246
Update routes 2023-06-12 05:14:03 -06:00
Daniel Supernault 49e5703198
Add ImportPostController 2023-06-12 05:13:50 -06:00
Daniel Supernault b64af89d40
Update migration and service 2023-06-12 04:56:18 -06:00
Daniel Supernault 7dd45c23b7
Add ImportStatus Resource 2023-06-12 02:52:00 -06:00
Daniel Supernault 8c9f4da48a
Add ImportPost model, migration and service 2023-06-12 02:51:29 -06:00
Daniel Supernault c50e0966db
Add import config 2023-06-12 02:22:00 -06:00
daniel 1dd9617da2
Merge pull request #4465 from pixelfed/staging
Add missing vue components + spa.js
2023-06-11 16:57:49 -06:00
Daniel Supernault 7217c962bf
Update webpack 2023-06-11 16:47:23 -06:00
Daniel Supernault 1f6d11736a
Add partial components 2023-06-11 16:38:28 -06:00
Daniel Supernault cbf086ccb4
Add spa.js 2023-06-11 16:24:37 -06:00
Daniel Supernault adf015be43
Update webpack 2023-06-11 16:20:05 -06:00
Daniel Supernault d48e8d9832
Add NavMenu component 2023-06-11 15:38:14 -06:00
Daniel Supernault 67bf3d10e0
Update components.js 2023-06-11 15:36:00 -06:00
Daniel Supernault 36f5f2e8b4
Update admin.js, add autospam + reports components 2023-06-11 15:35:34 -06:00
Daniel Supernault bffd8f0771
Add section components 2023-06-11 15:35:06 -06:00
Daniel Supernault 9817025578
Add Profile components 2023-06-11 15:32:44 -06:00
Daniel Supernault f5dbc8281a
Add partial components 2023-06-11 15:23:23 -06:00
Daniel Supernault 5361082026
Add partial components 2023-06-11 15:16:20 -06:00
Daniel Supernault fff692a25c
Add NotFound component 2023-06-11 15:05:06 -06:00
Daniel Supernault 751f30d845
Add Language component 2023-06-11 15:04:31 -06:00
Daniel Supernault add6cf0fe1
Add Home component 2023-06-11 15:03:36 -06:00
Daniel Supernault b12b956b7d
Add Hashtag component 2023-06-11 15:01:44 -06:00
Daniel Supernault b447db082f
Add Discover components 2023-06-11 15:01:02 -06:00
Daniel Supernault bb97b55c66
Add Direct+DirectMessage components 2023-06-11 14:57:06 -06:00
Daniel Supernault 495b78afba
Add changelog component 2023-06-11 14:55:43 -06:00
Daniel Supernault 163cf5b3c9
Add compose component 2023-06-11 14:54:40 -06:00
Daniel Supernault ab2a11341c
Add admin components 2023-06-11 14:53:37 -06:00
daniel 398a42b383
Merge pull request #4463 from pixelfed/staging
Update admin dashboard, improve users section
2023-06-10 05:09:27 -06:00
Daniel Supernault 7cd59f75e4
Update admin dashboard, improve users section 2023-06-10 05:08:16 -06:00
daniel 7736324ee7
Merge pull request #4462 from pixelfed/staging
Update admin dashboard, improve users section
2023-06-10 03:41:32 -06:00
Daniel Supernault 8be4582fc4
Update changelog 2023-06-10 03:41:23 -06:00
Daniel Supernault 36b6bf480e
Update admin dashboard, improve users section 2023-06-10 03:40:31 -06:00
daniel 341a032949
Merge pull request #4456 from pixelfed/staging
Update profile
2023-06-06 06:21:57 -06:00
Daniel Supernault 2cbf1acec3
Update profile 2023-06-06 06:21:24 -06:00
daniel 61346b104c
Merge pull request #4455 from pixelfed/staging
Handle Update.Person activities
2023-06-06 06:14:03 -06:00
Daniel Supernault 0f72b33c0e
Add tests 2023-06-06 06:12:24 -06:00
Daniel Supernault e3be04db2b
Update HandleUpdateActivity 2023-06-06 06:08:00 -06:00
Daniel Supernault e8d4ce1888
Update MediaStorageService 2023-06-06 06:01:49 -06:00
Daniel Supernault d6374cfe70
Update MediaStorageService 2023-06-06 05:54:49 -06:00
Daniel Supernault ab9a8ba314
Update Profile 2023-06-06 05:50:08 -06:00
Daniel Supernault 5bea903409
Update HandleUpdateActivity 2023-06-06 05:47:45 -06:00
Daniel Supernault 1bf3ad7ed9
Update HandleUpdateActivity 2023-06-06 05:47:17 -06:00
Daniel Supernault 31afaba3d0
Update ProfilePipeline 2023-06-06 05:21:44 -06:00
Daniel Supernault 0eb51ed74d
Update RemoteAvatarFetch command 2023-06-06 05:07:14 -06:00
Daniel Supernault 29961c4a80
Update ap inbox 2023-06-06 04:53:09 -06:00
daniel 2e79a89706
Merge pull request #4454 from pixelfed/staging
Staging
2023-06-06 01:57:34 -06:00
Daniel Supernault 8cc91babd7
Update changelog 2023-06-06 01:56:51 -06:00
Daniel Supernault a8453e7719
Update api routes, add DeprecatedEndpoint middleware 2023-06-06 01:55:28 -06:00
daniel 498d7fec80
Merge pull request #4450 from pixelfed/staging
Update nginx config
2023-06-04 08:15:12 -06:00
Daniel Supernault b49310a2d3
Update changelog 2023-06-04 08:14:51 -06:00
Daniel Supernault fbdc635829
Update nginx config 2023-06-04 08:13:08 -06:00
daniel 4c0c29ee37
Merge pull request #4445 from pixelfed/staging
Staging
2023-06-02 06:46:35 -06:00
Daniel Supernault fccd927c0b
Update changelog 2023-06-02 06:45:49 -06:00
Daniel Supernault fe8728c0ba
Update Direct Messages, fix api endpoint 2023-06-02 06:45:14 -06:00
daniel 97ce98b234
Merge pull request #4444 from pixelfed/staging
Staging
2023-06-02 05:37:29 -06:00
Daniel Supernault eaff1a7607
Update compiled assets 2023-06-02 05:36:37 -06:00
Daniel Supernault 6197a7b1e1
Update Notifications.vue component, fix filtering logic to prevent endless spinner 2023-06-02 05:36:00 -06:00
daniel ad88a07118
Merge pull request #4443 from pixelfed/staging
Update Notifications.vue component, fix filtering logic to prevent en…
2023-06-02 05:32:57 -06:00
Daniel Supernault 69a53d0fa1
Update changelog 2023-06-02 05:32:25 -06:00
Daniel Supernault b495918b3c
Update compiled assets 2023-06-02 05:32:10 -06:00
Daniel Supernault 3df9b53f4e
Update Notifications.vue component, fix filtering logic to prevent endless spinner 2023-06-02 05:30:11 -06:00
daniel 70e82038a7
Merge pull request #4436 from pixelfed/staging
v0.11.8
2023-05-29 03:56:15 -06:00
Daniel Supernault 9c0d0a31f2
Update changelog 2023-05-29 03:55:44 -06:00
Daniel Supernault 884e2f196f
Bump version 2023-05-29 03:54:16 -06:00
Daniel Supernault a3303b072b
Update composer deps 2023-05-29 03:52:15 -06:00
daniel caefbc9777
Merge pull request #4435 from pixelfed/staging
Added `/api/v1.1/accounts/app/settings` endpoint and UserAppSettings …
2023-05-29 03:49:19 -06:00
Daniel Supernault cd08348f4c
Update changelog 2023-05-29 03:48:59 -06:00
Daniel Supernault a2305d5fdc
Added `/api/v1.1/accounts/app/settings` endpoint and UserAppSettings model to store app specific settings 2023-05-29 03:43:25 -06:00
daniel ce960266f9
Merge pull request #4433 from pixelfed/staging
Added `following_since` attribute to `/api/v1/accounts/relationships`…
2023-05-29 00:10:15 -06:00
Daniel Supernault 5154869c8c
Update changelog 2023-05-29 00:10:04 -06:00
Daniel Supernault 992d910b9c
Added `following_since` attribute to `/api/v1/accounts/relationships` endpoint when `_pe=1` (pixelfed entity) parameter is present 2023-05-29 00:08:31 -06:00
daniel 696804d62e
Merge pull request #4428 from pixelfed/staging
Update Status model, increase max mentions, hashtags and links
2023-05-27 07:28:25 -06:00
Daniel Supernault d4f92da0e5
Fix test 2023-05-27 07:27:35 -06:00
Daniel Supernault ac9fb5f661
Update changelog 2023-05-27 07:24:08 -06:00
Daniel Supernault 1430f5328f
Update Status model, increase max mentions, hashtags and links 2023-05-27 07:23:20 -06:00
daniel 392ee2e290
Merge pull request #4427 from pixelfed/staging
Update nginx config
2023-05-26 23:41:38 -06:00
Daniel Supernault 8b06af3f6c
Update nginx config 2023-05-26 23:41:17 -06:00
daniel ab0d87e07e
Merge pull request #4426 from pixelfed/staging
Update nginx config
2023-05-26 23:33:33 -06:00
Daniel Supernault a456fa63c9
Update nginx config 2023-05-26 23:32:48 -06:00
daniel 718359941b
Merge pull request #4425 from pixelfed/staging
Update nginx config
2023-05-26 20:22:47 -06:00
Daniel Supernault 100a4771f8
Update changelog 2023-05-26 20:22:31 -06:00
Daniel Supernault ee3b6e09b0
Update nginx config 2023-05-26 20:20:58 -06:00
daniel e46ee70500
Merge pull request #4424 from pixelfed/staging
Update UpdateStatusService, fix formatting issue. Fixes #4423
2023-05-26 19:18:06 -06:00
Daniel Supernault fe05d79b9f
Update changelog 2023-05-26 19:17:38 -06:00
Daniel Supernault 4479055e1e
Update UpdateStatusService, fix formatting issue. Fixes #4423 2023-05-26 19:16:57 -06:00
daniel 5cfe8cd56a
Merge pull request #4420 from pixelfed/staging
Staging
2023-05-25 05:00:17 -06:00
Daniel Supernault 57d4f67b85
Update changelog 2023-05-25 04:57:55 -06:00
Daniel Supernault 1f2183ee67
Update StatusActivityPubDeliver, fix addressing 2023-05-25 04:57:17 -06:00
daniel 483010e0b0
Merge pull request #4419 from pixelfed/staging
Update StatusRemoteUpdatePipeline, fix typo
2023-05-25 03:43:59 -06:00
Daniel Supernault 52d076a6bd
Update changelog 2023-05-25 03:43:43 -06:00
Daniel Supernault 109d041908
Update StatusRemoteUpdatePipeline, fix typo 2023-05-25 03:42:43 -06:00
daniel b7ae41d4a8
Merge pull request #4418 from pixelfed/staging
Staging
2023-05-25 02:33:33 -06:00
Daniel Supernault 53e0df4169
Update compiled assets 2023-05-25 02:32:58 -06:00
Daniel Supernault fa6df8fbd8
Update changelog 2023-05-25 02:29:55 -06:00
Daniel Supernault 0f803446dd
Update EditHistoryModal, fix caption rendering 2023-05-25 02:27:03 -06:00
daniel b15d939163
Merge pull request #4417 from pixelfed/staging
Staging
2023-05-25 02:05:50 -06:00
Daniel Supernault 58523b6d98
Update changelog 2023-05-25 02:04:24 -06:00
Daniel Supernault c0190d8436
Update Config, bump version for post edit support without having to clear cache 2023-05-25 02:03:59 -06:00
daniel ae38f2d8a6
Merge pull request #4416 from pixelfed/staging
Add Post Edits/Updates
2023-05-25 01:58:08 -06:00
Daniel Supernault 18c744c458
Update changelog 2023-05-25 01:50:29 -06:00
Daniel Supernault a6f96b4bb3
Remove temp logging 2023-05-25 01:50:15 -06:00
Daniel Supernault d4e17f75b4
Update config, enable post edits by default 2023-05-25 01:47:05 -06:00
Daniel Supernault f57d22ee66
Update compiled assets 2023-05-25 01:41:01 -06:00
Daniel Supernault 2a10809fa3
Update EditHistoryModal, use proper timestamp 2023-05-25 01:40:21 -06:00
Daniel Supernault 6bf3142e8b
Add Profile 2023-05-25 01:35:06 -06:00
Daniel Supernault 8b3d1eeb5e
Add Purify 2023-05-25 01:32:58 -06:00
Daniel Supernault 559978db46
Fix typo 2023-05-25 01:30:58 -06:00
Daniel Supernault 859ed621b0
Fix typo 2023-05-25 01:26:54 -06:00
Daniel Supernault b0634bfd8f
Add temp logging 2023-05-25 01:23:57 -06:00
Daniel Supernault aad4259a47
Update ap inbox 2023-05-25 01:13:43 -06:00
Daniel Supernault 734136a7e7
Add StatusRemoteUpdatePipeline job 2023-05-25 00:57:35 -06:00
Daniel Supernault ced085bc1d
Update compiled assets 2023-05-25 00:36:01 -06:00
Daniel Supernault 98cf8f32a0
Add Post Edits/Updates 2023-05-25 00:33:44 -06:00
daniel 53013dc32d
Merge pull request #4415 from pixelfed/staging
Add migration
2023-05-25 00:05:17 -06:00
Daniel Supernault 013656000c
Add migration 2023-05-25 00:04:47 -06:00
daniel f4e7ca6b0e
Merge pull request #4414 from pixelfed/staging
Staging
2023-05-25 00:03:58 -06:00
Daniel Supernault fb77ee764b
Add status_edits migration 2023-05-25 00:03:00 -06:00
Daniel Supernault f32eabdf19
Add migration 2023-05-25 00:01:50 -06:00
daniel c47de37c16
Merge pull request #4413 from pixelfed/staging
Update StatusService, fix bug in getFull method
2023-05-24 23:42:46 -06:00
Daniel Supernault 86e20e927d
Update changelog 2023-05-24 23:42:35 -06:00
Daniel Supernault 4d8b4dcf35
Update StatusService, fix bug in getFull method 2023-05-24 23:41:48 -06:00
daniel 8068f1eb0a
Merge pull request #4412 from pixelfed/staging
v0.11.7 🥳
2023-05-24 21:15:37 -06:00
Daniel Supernault 88f7dc26bb
Update changelog 2023-05-24 21:15:00 -06:00
Daniel Supernault c8eff6dff0
Bump version 2023-05-24 21:14:41 -06:00
Daniel Supernault 808e89328c
Update composer deps 2023-05-24 21:13:00 -06:00
daniel dba21ef92f
Merge pull request #4257 from intentionally-left-nil/fix-oauth-diagnostics
Fix diagnostic confirmation of passport oauth key
2023-05-24 21:05:55 -06:00
daniel 1b1378ac64
Merge pull request #4255 from intentionally-left-nil/fix-reset-cache-ordering
Run config:cache first
2023-05-24 21:03:45 -06:00
daniel a73541ab95
Merge pull request #4409 from pixelfed/staging
Update login form, allow admins to enable captcha after X failed atte…
2023-05-23 05:10:15 -06:00
Daniel Supernault 566787c2b7
Update changelog 2023-05-23 05:09:47 -06:00
Daniel Supernault 221ddce0fa
Update login form, allow admins to enable captcha after X failed attempts. Admins can set the number of attempts before captcha is shown, default is 2 attempts before captcha is required 2023-05-23 05:08:34 -06:00
daniel 9d763bded1
Merge pull request #4408 from pixelfed/staging
Update login/register views and captcha config, enable login or regis…
2023-05-23 03:54:25 -06:00
Daniel Supernault f9eb99c897
Update changelog 2023-05-23 03:54:07 -06:00
Daniel Supernault c071c7195e
Update login/register views and captcha config, enable login or register captchas or both 2023-05-23 03:52:34 -06:00
daniel 9a8a840b4e
Merge pull request #4407 from pixelfed/staging
Update AP Inbox, fix delete handling
2023-05-23 02:55:33 -06:00
Daniel Supernault 39a42c637f
Update changelog 2023-05-23 02:54:53 -06:00
Daniel Supernault 2800c8886a
Update AP Inbox, fix delete handling 2023-05-23 02:54:02 -06:00
daniel 24afe169ad
Merge pull request #4406 from pixelfed/staging
Update PublicTimelineService, improve warmCache query
2023-05-23 01:30:46 -06:00
Daniel Supernault 361d5f0374
Update changelog 2023-05-23 01:30:29 -06:00
Daniel Supernault 9f901d65c9
Update PublicTimelineService, improve warmCache query 2023-05-23 01:29:22 -06:00
daniel ce8470562f
Merge pull request #4405 from pixelfed/staging
Update ApiV1Controller, add idempotency-key support to /api/v1/status…
2023-05-23 00:48:22 -06:00
Daniel Supernault cbe9458244
Update changelog 2023-05-23 00:48:14 -06:00
Daniel Supernault c54cdd3eb4
Update ApiV1Controller, add idempotency-key support to /api/v1/statuses endpoint 2023-05-23 00:46:57 -06:00
daniel 01c0902b3d
Merge pull request #4404 from pixelfed/staging
Update MediaController, add fallback for local files that are later s…
2023-05-22 18:12:55 -06:00
Daniel Supernault f452509e17
Update changelog 2023-05-22 18:12:41 -06:00
Daniel Supernault 4973cb4611
Update MediaController, add fallback for local files that are later stored on S3 but still are referenced in cached objects remotely 2023-05-22 18:10:55 -06:00
daniel 79642bea7e
Merge pull request #4401 from pixelfed/staging
Update scheduler, fix S3 media garbage collection not being executed …
2023-05-21 07:36:14 -06:00
Daniel Supernault a1ab88a8ca
Update changelog 2023-05-21 07:36:02 -06:00
Daniel Supernault adb070f178
Update scheduler, fix S3 media garbage collection not being executed when cloud storage is enabled via dashboard without .env/config being enabled 2023-05-21 07:34:27 -06:00
daniel c96f0008e1
Merge pull request #4400 from pixelfed/staging
Update admin dashboard, add mass account deletes
2023-05-21 06:47:23 -06:00
Daniel Supernault 0abc5723bc
Update changelog 2023-05-21 06:46:20 -06:00
Daniel Supernault b8426ccea7
Update admin dashboard, add mass account deletes 2023-05-21 06:44:15 -06:00
daniel 7b5949f55d
Merge pull request #4399 from pixelfed/staging
Staging
2023-05-21 01:54:24 -06:00
Daniel Supernault 12ea6f1950
Update changelog 2023-05-21 01:53:45 -06:00
Daniel Supernault c6ffda9618
Update admin moderation logic, only re-add top level posts 2023-05-21 01:53:25 -06:00
daniel 9279771e94
Merge pull request #4398 from pixelfed/staging
Staging
2023-05-20 22:59:23 -06:00
Daniel Supernault 79bcaadd49
Update ApiV1Controller, filter muted/blocked accounts from tag timeline. 2023-05-20 22:59:11 -06:00
Daniel Supernault 607c64ae72
Update changelog 2023-05-20 22:58:03 -06:00
Daniel Supernault f42c114058
Update ApiV1Controller, filter muted/blocked accounts from tag timeline. 2023-05-20 22:57:42 -06:00
daniel a36d2b1d2b
Merge pull request #4396 from pixelfed/staging
Staging
2023-05-19 04:57:09 -06:00
Daniel Supernault 2b6cf7f8de
Update changelog 2023-05-19 04:55:59 -06:00
Daniel Supernault 3662d3defe
Update Settings, allow users to disable atom feeds 2023-05-19 04:53:52 -06:00
daniel eaf94cab0c
Merge pull request #4395 from pixelfed/staging
Staging
2023-05-19 03:43:05 -06:00
Daniel Supernault 6622a851bb
Update changelog 2023-05-19 03:41:45 -06:00
Daniel Supernault dd2f5bb96a
Update ProfileController, require login to view spam accounts, and disable profile embeds for spam accounts 2023-05-19 03:41:01 -06:00
Daniel Supernault c167af43a4
Update StatusController, disable embeds from spam accounts 2023-05-19 03:40:00 -06:00
Daniel Supernault 63b72c429c
Update atom view, fix atom feed bug 2023-05-19 03:38:11 -06:00
daniel c8a34a4dbd
Merge pull request #4393 from pixelfed/staging
Update AdminReportController
2023-05-18 22:34:49 -06:00
Daniel Supernault ae0d5d2d40
Update AdminReportController 2023-05-18 22:34:00 -06:00
daniel 92d2533068
Merge pull request #4391 from pixelfed/staging
Staging
2023-05-17 05:04:22 -06:00
Daniel Supernault 46ccbf2bfd
Update changelog 2023-05-17 05:03:26 -06:00
Daniel Supernault 18cddd43f3
Update Bouncer, adjust advanced Autospam logic 2023-05-17 05:03:03 -06:00
daniel 6db781aa91
Merge pull request #4390 from pixelfed/staging
Staging
2023-05-17 04:45:57 -06:00
Daniel Supernault a11e1ee3f8
Update AutospamUpdateCachedDataPipeline 2023-05-17 04:44:35 -06:00
Daniel Supernault 5abc2445a7
Update AutospamUpdateCachedDataPipeline 2023-05-17 04:34:30 -06:00
daniel 3fd334da0e
Merge pull request #4389 from pixelfed/staging
Autospam Advanced Detection
2023-05-17 04:21:21 -06:00
Daniel Supernault 053b30bca0
Update nav and Bouncer 2023-05-17 04:10:07 -06:00
Daniel Supernault d1880ee6b8
Update changelog 2023-05-17 04:00:22 -06:00
Daniel Supernault 132a58de54
Add Autospam Advanced Detection 2023-05-17 04:00:03 -06:00
Daniel Supernault 75db5116b7
Add AutospamCustomTokens model + migration 2023-05-17 03:53:05 -06:00
daniel acde00283c
Merge pull request #4383 from pixelfed/staging
Staging
2023-05-15 06:16:22 -06:00
Daniel Supernault 127343ccda
Update changelog 2023-05-15 06:15:42 -06:00
Daniel Supernault ad25ed6792
Update UserCreate command, fix is_admin flag 2023-05-15 06:15:09 -06:00
daniel dd103fd600
Merge pull request #4380 from pixelfed/staging
Staging
2023-05-14 20:32:53 -06:00
Daniel Supernault af35fe93ac
Update changelog 2023-05-14 20:16:31 -06:00
Daniel Supernault 6a2daf1f63
Update ApiControllers, remove cloud ip blocks 2023-05-14 20:15:41 -06:00
daniel e3bf01c5a2
Merge pull request #4378 from pixelfed/staging
Update CommentPipeline, replace expensive query and mark for refactor
2023-05-13 17:38:33 -06:00
Daniel Supernault 764315666e
Update CommentPipeline, replace expensive query and mark for refactor 2023-05-13 17:37:56 -06:00
daniel cac9041e3d
Merge pull request #4377 from pixelfed/staging
Update Bouncer
2023-05-13 17:11:55 -06:00
Daniel Supernault 8b007f9ee9
Update ap helpers 2023-05-13 17:11:27 -06:00
Daniel Supernault 56ec083db5
Update Bouncer 2023-05-13 17:03:33 -06:00
Anil Kulkarni 91660a2477
Run config:cache first
When updating config variables, such as the APP_URL, the values need to be propagated before calling e.g. route:cache/view:cache
Otherwise those commands will first run with the old config cache
2023-05-13 07:22:40 -07:00
daniel dd7cb8b597
Merge pull request #4376 from pixelfed/staging
Add migration
2023-05-13 07:05:02 -06:00
Daniel Supernault 83bc84dd97
Add migration 2023-05-13 07:04:11 -06:00
daniel 5a8e0ab5df
Merge pull request #4375 from pixelfed/staging
Staging
2023-05-13 07:02:16 -06:00
Daniel Supernault 95f118b2dc
Add migration 2023-05-13 07:01:26 -06:00
Daniel Supernault a91a5e4872
Update StatusEntityLexer, stop saving entities 2023-05-13 06:28:17 -06:00
daniel c643dc307b
Merge pull request #4374 from pixelfed/staging
Staging
2023-05-13 06:08:56 -06:00
Daniel Supernault 4f23c250f4
Update compiled assets 2023-05-13 06:06:12 -06:00
Daniel Supernault a1bd044c0c
Update Notifications component 2023-05-13 06:05:54 -06:00
Daniel Supernault 8a7e5f7b2b
Update changelog 2023-05-13 05:47:15 -06:00
Daniel Supernault 8153e7a746
Update compiled assets 2023-05-13 05:46:38 -06:00
Daniel Supernault 588ca653a8
Update admin autospam apis, remove autospam warning notifications when appropriate 2023-05-13 05:44:46 -06:00
Daniel Supernault d5f63f8a71
Update AutoSpam Bouncer, generate notification on positive detections 2023-05-13 05:41:09 -06:00
Daniel Supernault 0d3b4bc225
Update Notification components, add autospam notification support 2023-05-13 05:39:47 -06:00
Daniel Supernault ea943333a5
Update models, remove deprecated toText and toHtml methods 2023-05-13 00:43:46 -06:00
Daniel Supernault 58ec49fd57
Update InstanceService, fix banner blurhash memory bug 2023-05-13 00:18:53 -06:00
Daniel Supernault 026842dd93
Update InstanceService, fix banner blurhash memory bug 2023-05-13 00:16:50 -06:00
Daniel Supernault 3aad75abcf
Update InstanceService, fix banner blurhash memory bug 2023-05-12 23:56:55 -06:00
Daniel Supernault 6cdb5bc672
Update Notification logic, remove message and rendered fields 2023-05-12 23:15:41 -06:00
daniel 1dd962eaa6
Merge pull request #4371 from pixelfed/staging
Add /api/v2/instance api endpoint
2023-05-11 04:11:23 -06:00
Daniel Supernault eb5bb9fede
Update changelog 2023-05-11 04:08:46 -06:00
Daniel Supernault 167dbcdd43
Add /api/v2/instance api endpoint 2023-05-11 04:06:27 -06:00
Anil Kulkarni b76ea33890
Fix diagnostic confirmation of passport oauth key
The ouath public/private keys can either be specified as a file or as an environment variable.
Check both places in the diagnostic page
2023-05-11 02:53:45 -07:00
daniel 03e7e24b6b
Merge pull request #4363 from pixelfed/staging
Staging
2023-05-09 04:09:18 -06:00
Daniel Supernault e559bdbf57
Update changelog 2023-05-09 04:05:54 -06:00
Daniel Supernault bd30e671cc
Update compiled assets 2023-05-09 04:05:26 -06:00
Daniel Supernault 0b42fe0f00
Update HashtagService 2023-05-09 03:22:28 -06:00
Daniel Supernault b5fe956acf
Add `only_media` support to /api/v1/timelines/tag/:id api endpoint 2023-05-09 02:13:26 -06:00
Daniel Supernault ed35214161
Update HashtagFollow model, add MAX_LIMIT of 250 tags per account 2023-05-09 02:05:23 -06:00
Daniel Supernault 1a83c5858d
Update settings view, add fallback avatar 2023-05-09 01:43:58 -06:00
Daniel Supernault 521b3b4c82
Add /api/v1/tags/:id api endpoint 2023-05-09 01:25:39 -06:00
Daniel Supernault b4ad6668e9
Update api routes 2023-05-09 01:10:09 -06:00
Daniel Supernault 4d997bb959
Add /api/v1/tags/:id/follow and /api/v1/tags/:id/unfollow api endpoints 2023-05-09 01:08:09 -06:00
Daniel Supernault 175a848665
Add /api/v1/followed_tags api endpoint 2023-05-09 00:21:46 -06:00
Daniel Supernault fc1a385cfd
Update ApiV1Controller, fix hashtag timeline 2023-05-08 22:50:49 -06:00
Daniel Supernault 43d3aa2b94
Update StatusTransformers, fix ProfileService to soft fail on missing or deleted accounts 2023-05-08 22:49:08 -06:00
Daniel Supernault 91ba139808
Update LikeService, improve likedBy logic to soft fail on missing or deleted accounts 2023-05-08 22:47:59 -06:00
Daniel Supernault df444851b5
Update MediaTagService, fix ProfileService to soft fail on missing or deleted accounts 2023-05-08 22:47:19 -06:00
Daniel Supernault 6bc20a37ed
Update ProfileService, add softFail param 2023-05-08 22:45:57 -06:00
Daniel Supernault f48daab37e
Update StatusService, fix missing account condition 2023-05-08 22:45:02 -06:00
daniel c996f37366
Merge pull request #4360 from pixelfed/staging
Staging
2023-05-07 05:26:41 -06:00
Daniel Supernault db2da84bec
Update LandingController, fix config_cache api check 2023-05-07 05:25:38 -06:00
Daniel Supernault a0e299c28d
Update changelog 2023-05-07 05:22:28 -06:00
Daniel Supernault 6a2e9e8f7d
Update config, enable oauth by default 2023-05-07 05:22:07 -06:00
Daniel Supernault add5eaf094
Fix CI 2023-05-07 05:20:33 -06:00
Daniel Supernault 637cdca27a
Fix CI 2023-05-07 05:18:59 -06:00
Daniel Supernault eda7607fe8
Update CI 2023-05-07 05:15:29 -06:00
Daniel Supernault 9b054d2bae
Update CI .env 2023-05-07 05:12:47 -06:00
Daniel Supernault ccbba56633
Update AdminSettingsController 2023-05-07 05:08:45 -06:00
Daniel Supernault 6ea2bdc782
Update Admin Dashboard, allow admins to designate an admin account for the landing page and instance api endpoint 2023-05-07 05:04:18 -06:00
Daniel Supernault 970f77b078
Update instance config, enable config cache by default 2023-05-07 04:47:13 -06:00
daniel 4a97e8003f
Merge pull request #4359 from pixelfed/staging
Update migration
2023-05-07 02:44:01 -06:00
Daniel Supernault 4b309d1acf
Update migration 2023-05-07 02:42:16 -06:00
daniel 56f64ad5f3
Merge pull request #4358 from pixelfed/staging
Staging
2023-05-07 01:43:36 -06:00
Daniel Supernault 51768083fe
Added store remote media on S3 config setting, disabled by default 2023-05-07 01:35:51 -06:00
Daniel Supernault 37bd2ee51b
Update ApiV1Controller, fix trending accounts format. Closes #4356 2023-05-06 18:49:21 -06:00
daniel e4db35c6dd
Merge pull request #4355 from pixelfed/staging
Staging
2023-05-05 06:45:06 -06:00
Daniel Supernault a96a3cfc31
Update changelog 2023-05-05 06:43:25 -06:00
Daniel Supernault e297efcae3
Update compiled assets 2023-05-05 06:43:06 -06:00
Daniel Supernault 633351f6dc
Update StoryController, show active self stories on home timeline 2023-05-05 06:42:17 -06:00
daniel 2b5437a3b9
Merge pull request #4354 from pixelfed/staging
Staging
2023-05-05 03:51:17 -06:00
Daniel Supernault 73429ac975
Update changelog 2023-05-05 03:50:14 -06:00
Daniel Supernault 6bc3bcbef5
Update compiled assets 2023-05-05 03:50:07 -06:00
daniel 7e2bad3df6
Merge pull request #4353 from pixelfed/staging
Staging
2023-05-05 03:06:32 -06:00
Daniel Supernault e6250db0de
Update compiled assets 2023-05-05 03:05:25 -06:00
Daniel Supernault 11552d1273
Update SearchApiV2Service 2023-05-05 03:04:56 -06:00
Daniel Supernault d15bd60d87
Update default avatar, optimize png 2023-05-05 03:04:04 -06:00
Daniel Supernault 666e5732a5
Update SearchApiV2Service, improve postgres support 2023-05-05 02:10:07 -06:00
daniel bc7d431369
Merge pull request #4351 from pixelfed/staging
Staging
2023-05-05 00:05:03 -06:00
Daniel Supernault d34f078887
Update changelog 2023-05-05 00:04:02 -06:00
Daniel Supernault fce14f6568
Update compiled assets 2023-05-05 00:03:46 -06:00
Daniel Supernault 8ef900bf8e
Update sudo mode view, fix trusted device checkbox 2023-05-05 00:01:07 -06:00
Daniel Supernault dac0d08319
Update admin dashboard, fix search and dropdown menu 2023-05-05 00:00:11 -06:00
daniel b289cecd3d
Merge pull request #4344 from pixelfed/staging
Bump version 0.11.5 => 0.11.6
2023-05-03 00:30:22 -06:00
Daniel Supernault 9c7b247819
Bump version 0.11.5 => 0.11.6 2023-05-03 00:29:47 -06:00
daniel 6c5b93cc50
Merge pull request #4343 from pixelfed/staging
Update AdminController, fix custom emoji domain search on postgres. C…
2023-05-03 00:11:40 -06:00
Daniel Supernault 623f62ef71
Update changelog 2023-05-03 00:11:20 -06:00
Daniel Supernault 3dac45f388
Update AdminController, fix custom emoji domain search on postgres. Closes #4333 2023-05-03 00:10:08 -06:00
daniel c316e488fa
Merge pull request #4342 from pixelfed/staging
Staging
2023-05-02 23:35:34 -06:00
Daniel Supernault 96d4486abb
Update changelog 2023-05-02 23:34:52 -06:00
Daniel Supernault 64059cb47e
Add postgres migration to fix duplicate hashtags 2023-05-02 23:34:06 -06:00
Daniel Supernault 6e20d0a670
Update SearchApiV2Service, fix postgres hashtag search and prepend wildcard operator to improve results 2023-05-02 23:33:06 -06:00
Daniel Supernault 867cbc757c
Update StatusTagsPipeline, deduplicate hashtags on postgres 2023-05-02 23:31:16 -06:00
Daniel Supernault 055aa6b39f
Update ApiV1Controller and DiscoverController, fix postgres hashtag search 2023-05-02 23:26:37 -06:00
Daniel Supernault 55293e9ee6
Update changelog 2023-05-02 21:02:13 -06:00
Daniel Supernault c1d1272105
Update pixelfed config, disable cloud ip bans by default 2023-05-02 21:01:59 -06:00
daniel 684785500a
Merge pull request #4341 from pixelfed/staging
Staging
2023-05-02 20:56:43 -06:00
Daniel Supernault 79b6a17e44
Add postgres migration 2023-05-02 20:55:45 -06:00
Daniel Supernault 670b3ba868
Update passport config 2023-05-02 20:53:10 -06:00
Daniel Supernault d563901622
Update ApiV1Controller 2023-05-02 20:11:26 -06:00
daniel 61435d66c3
Merge pull request #4335 from pixelfed/staging
Staging
2023-04-29 20:06:19 -06:00
Daniel Supernault 39d39aa594
Update changelog 2023-04-29 20:05:57 -06:00
Daniel Supernault a93a3efd21
Update Inbox, remove handleCreateActivity logic that rejected posts from accounts without followers 2023-04-29 20:05:32 -06:00
daniel d40d48aa76
Merge pull request #4331 from pixelfed/staging
Staging
2023-04-26 03:17:32 -06:00
Daniel Supernault aceb45ae50
Update changelog 2023-04-26 03:10:45 -06:00
Daniel Supernault 0ab5b96a00
Update ResetPasswordController, add captcha support, improve security and a new redesigned view 2023-04-26 03:10:06 -06:00
Daniel Supernault f6e7ff649e
Update ForgotPasswordController, add captcha support, improve security and a new redesigned view 2023-04-26 03:08:20 -06:00
Daniel Supernault 091e0b2cb2
Update sudo mode checkpoint view design 2023-04-26 02:56:59 -06:00
Daniel Supernault 86c472ac19
Update 2fa checkpoint view design 2023-04-26 02:39:20 -06:00
Daniel Supernault 506f95c6b1
Update SecuritySettings, remove imagick depdency for 2FA qr code generation image 2023-04-26 02:34:44 -06:00
daniel 7ad48807f1
Merge pull request #4328 from pixelfed/staging
Add Federated Reports
2023-04-26 02:30:52 -06:00
Daniel Supernault aaf5b2764e
Update Inbox, improve Flag report handling 2023-04-25 20:36:53 -06:00
Daniel Supernault 0f1f3b1fd3
Update inbox 2023-04-24 06:37:51 -06:00
Daniel Supernault 28717a2561
Update inbox 2023-04-24 06:34:58 -06:00
Daniel Supernault e7f7072349
Update inbox 2023-04-24 06:32:22 -06:00
Daniel Supernault 383c6fe8ee
Add Federated Reports 2023-04-24 06:26:57 -06:00
daniel edbc3ce1b4
Merge pull request #4327 from pixelfed/staging
Staging
2023-04-24 04:00:19 -06:00
Daniel Supernault c294368b8f
Update changelog 2023-04-24 03:59:55 -06:00
Daniel Supernault 1bbee6d07b
Update RegisterController, improve max_users calculation and add kb page to redirect to if conditions are met 2023-04-24 03:58:34 -06:00
daniel 85cdaf5f3a
Merge pull request #4326 from eitch/staging
[Fix] Don't count inactive accounts for registration blocking
2023-04-24 03:33:55 -06:00
daniel 0e5d3595e1
Merge pull request #4325 from pixelfed/staging
Update mailers
2023-04-24 03:27:24 -06:00
Daniel Supernault 7d14e07901
Update mailers 2023-04-24 03:26:36 -06:00
Robert von Burg 7e8938d749
[Fix] Don't count inactive accounts for registration blocking 2023-04-24 11:26:36 +02:00
daniel 3b8072cde8
Merge pull request #4323 from pixelfed/staging
Redesigned Admin Dashboard Reports/Moderation
2023-04-24 01:55:23 -06:00
Daniel Supernault 4fedfbe8fd
Update changelog 2023-04-24 01:55:10 -06:00
Daniel Supernault c3ec81e525
Update compiled assets 2023-04-24 01:54:26 -06:00
Daniel Supernault c6cc6327d3
Redesigned Admin Dashboard Reports/Moderation 2023-04-24 01:52:59 -06:00
daniel 8858960577
Merge pull request #4322 from pixelfed/staging
Update pipeline dispatch, replace dispatchSync with dispatch
2023-04-24 01:36:39 -06:00
Daniel Supernault ae6dd4e3a2
Update pipeline dispatch, replace dispatchSync with dispatch 2023-04-24 01:36:06 -06:00
daniel 4e6fa82d53
Merge pull request #4314 from pixelfed/staging
Staging
2023-04-20 05:29:41 -06:00
Daniel Supernault 1a12ba51d6
Update changelog 2023-04-20 05:28:28 -06:00
Daniel Supernault 9873913921
Update ApiV1Controller, fix account blocks.Closes #4304 2023-04-20 05:27:44 -06:00
daniel c4941365e8
Merge pull request #4313 from pixelfed/staging
Update RegisterController, store client ip during registration
2023-04-20 01:55:46 -06:00
Daniel Supernault 183bfb916a
Update changelog 2023-04-20 01:55:31 -06:00
Daniel Supernault d4c967de98
Update RegisterController, store client ip during registration 2023-04-20 01:54:23 -06:00
daniel 8a8bde9bcf
Merge pull request #4312 from pixelfed/staging
Ban cloud ip ranges
2023-04-20 01:12:51 -06:00
Daniel Supernault 33e6cf26d2
Update changelog 2023-04-20 01:09:37 -06:00
Daniel Supernault 50ab2e2039
Add cloud ip bans to BouncerService 2023-04-20 01:08:54 -06:00
daniel 8ee537ed74
Merge pull request #4311 from pixelfed/staging
Staging
2023-04-20 00:02:09 -06:00
Daniel Supernault d878672368
Update changelog 2023-04-20 00:01:18 -06:00
Daniel Supernault 849e510311
Update db:raw queries to support laravel v10 2023-04-20 00:00:55 -06:00
daniel 135792d543
Merge pull request #4310 from pixelfed/staging
Update http kernel
2023-04-19 23:29:08 -06:00
Daniel Supernault 848181913f
Update http kernel 2023-04-19 23:28:42 -06:00
daniel ddc7b82683
Merge pull request #4309 from pixelfed/staging
Add php 8.2 support. Bump laravel version, v9 => v10
2023-04-19 23:11:39 -06:00
Daniel Supernault 4fa1f2d1e7
Update tests 2023-04-19 23:10:12 -06:00
Daniel Supernault 9558da4873
Bump circleci php version 2023-04-19 23:06:44 -06:00
Daniel Supernault 932f8956ba
Bump circleci php version 2023-04-19 22:56:38 -06:00
Daniel Supernault c09a2fae8b
Update changelog 2023-04-19 22:42:12 -06:00
Daniel Supernault fb4ac4ebd6
Add php 8.2 support. Bump laravel version, v9 => v10 2023-04-19 22:41:26 -06:00
Daniel Supernault b1f95a7213
Bump laravel, v9 => v10 2023-04-19 22:30:37 -06:00
Daniel Supernault 66c0b3577f
Update HttpSignature 2023-04-19 19:09:55 -06:00
Daniel Supernault 9ffe5941d7
Update HttpSignature 2023-04-19 19:04:41 -06:00
Daniel Supernault f17ca9d904
Update HttpSignature 2023-04-19 18:57:40 -06:00
daniel d47f2f965c
Merge pull request #4295 from pixelfed/staging
Staging
2023-04-11 22:13:41 -06:00
Daniel Supernault b43f06abaf
Update changelog 2023-04-11 22:12:42 -06:00
Daniel Supernault 6d59dc8e1e
Update site config 2023-04-11 22:12:09 -06:00
daniel 2451593bb9
Merge pull request #4291 from pixelfed/staging
Update LandingController
2023-04-09 20:35:11 -06:00
Daniel Supernault 385c166944
Update LandingController 2023-04-09 20:34:50 -06:00
daniel 6092005521
Merge pull request #4290 from pixelfed/staging
Staging
2023-04-09 20:22:41 -06:00
Daniel Supernault 4d41d169f3
Update changelog 2023-04-09 20:22:07 -06:00
Daniel Supernault 634c15e486
Update Privacy Settings, add Directory setting 2023-04-09 20:21:40 -06:00
daniel 8df792cb54
Merge pull request #4289 from pixelfed/staging
Staging
2023-04-09 20:07:35 -06:00
Daniel Supernault 2683fdaf06
Update changelog 2023-04-09 20:06:37 -06:00
Daniel Supernault b716926b9c
Update LandingController, fix configonfig variable names 2023-04-09 20:06:17 -06:00
daniel 2d63b8064b
Merge pull request #4288 from pixelfed/staging
Add webfont
2023-04-09 20:00:46 -06:00
Daniel Supernault 25f8b9ae62
Add webfont 2023-04-09 20:00:26 -06:00
daniel eba44b62e6
Merge pull request #4287 from pixelfed/staging
Update ImageOptimizePipeline, improve support for disabling image opt…
2023-04-09 16:11:04 -06:00
Daniel Supernault e76289e4e2
Update ImageOptimizePipeline, improve support for disabling image optimizations 2023-04-09 16:09:17 -06:00
daniel b28cc54e03
Merge pull request #4286 from pixelfed/staging
Staging
2023-04-09 14:43:38 -06:00
Daniel Supernault 153cea4783
Update changelog 2023-04-09 14:42:58 -06:00
Daniel Supernault 780f250742
Update LandingService, enable landing directory/explore feed by default and move configuration to config/instance.php file 2023-04-09 14:40:16 -06:00
daniel 4b6141614a
Merge pull request #4285 from pixelfed/staging
Staging
2023-04-09 12:23:55 -06:00
Daniel Supernault c2805ebe92
Update changelog 2023-04-09 12:22:28 -06:00
Daniel Supernault 76be49ac58
Update admin autospam/report email templates, remove image previews 2023-04-09 12:22:03 -06:00
Daniel Supernault dca48fd84b
Update compiled assets 2023-04-09 12:17:19 -06:00
Daniel Supernault 3ba32edfc9
Update landing styles 2023-04-09 12:16:52 -06:00
Daniel Supernault 738925c20d
Update StatusController, allow users to delete replies to posts 2023-04-09 12:05:59 -06:00
Daniel Supernault 50f0fd27e2
Update compiled assets 2023-04-09 12:04:56 -06:00
daniel 8ac41196ac
Merge pull request #4284 from pixelfed/staging
Staging
2023-04-08 18:32:17 -06:00
Daniel Supernault 1b53174d3a
Update StatusController 2023-04-08 18:31:29 -06:00
Daniel Supernault 2eee36cfbd
Update MediaS3GarbageCollector, fix handle 2023-04-08 04:25:52 -06:00
daniel 157ec1b5ad
Merge pull request #4282 from pixelfed/staging
New Landing Page Design
2023-04-08 00:13:16 -06:00
Daniel Supernault 102e71310d
Update changelog 2023-04-08 00:10:22 -06:00
Daniel Supernault 79a3eab65e
Update compiled assets 2023-04-08 00:04:24 -06:00
Daniel Supernault 48f3d2c397
Update landing components 2023-04-08 00:04:01 -06:00
Daniel Supernault 5465f2834f
Add new fonts 2023-04-07 23:31:42 -06:00
Daniel Supernault 4fcb70da23
Update compiled assets 2023-04-07 23:29:39 -06:00
Daniel Supernault 6d3c7afd2b
Update landing view 2023-04-07 23:14:08 -06:00
Daniel Supernault 09c0032b39
New landing page design 2023-04-07 22:35:51 -06:00
Daniel Supernault e7a19b2c18
Add admin landing settings view 2023-04-07 21:34:14 -06:00
Daniel Supernault 87d8f516f9
Add admin landing settings 2023-04-07 20:22:10 -06:00
daniel 21714eec34
Merge pull request #4278 from pixelfed/staging
Staging
2023-04-04 03:56:35 -06:00
Daniel Supernault 014a4d9c51
Update changelog 2023-04-04 03:54:08 -06:00
Daniel Supernault 573c88d7b5
Update filesystem config, change FILESYSTEM_DRIVER env variable to DANGEROUSLY_SET_FILESYSTEM_DRIVER and remove from default env configs. Changing the default filesystem should be avoided, use FILESYSTEM_CLOUD for s3 support, otherwise you can break things 2023-04-04 03:52:32 -06:00
Daniel Supernault 672cccd433
Add media:fix-nonlocal-driver command. Fixes s3 media created with invalid FILESYSTEM_DRIVER=s3 configuration 2023-04-04 03:46:29 -06:00
Daniel Supernault 28bf8649b3
Update Profile model, fix avatar url path generation. Fixes #4041, Fixes #4031, Fixes #3523 2023-04-04 02:35:37 -06:00
daniel 21685d0baf
Merge pull request #4276 from pixelfed/staging
Staging
2023-04-03 19:47:31 -06:00
Daniel Supernault 22da2647c7
Update filesystems, store all files as public by default and add default permissions. Fixes #4273, #4275. Closes #3825 2023-04-03 19:15:20 -06:00
Daniel Supernault 73aa01e8e0
Update ApiV1Controller, filter mute/blocks on statuses/context and statuses/replies endpoints 2023-04-03 18:51:20 -06:00
daniel c1c371ec6f
Merge pull request #4270 from pixelfed/staging
Staging
2023-04-02 05:51:53 -06:00
Daniel Supernault 988818be69
Update changelog 2023-04-02 05:49:23 -06:00
Daniel Supernault 855e9626a5
Update Story v1.1 api endpoints 2023-04-02 05:48:25 -06:00
Daniel Supernault aebbad964b
Update MediaPathService, fix story path 2023-04-02 05:15:38 -06:00
Daniel Supernault 4d72b9e3ae
Update app.js, add title attribute to iframe embeds to comply with accessibility requirements 2023-03-31 01:31:48 -06:00
daniel 8458eb9c7e
Merge pull request #4263 from pixelfed/staging
Update ComposeController, fix postgres location search. Closes #4242
2023-03-29 02:18:21 -06:00
Daniel Supernault 7bf4f8d6ac
Update changelog 2023-03-29 02:17:43 -06:00
Daniel Supernault 64a4a0060a
Update ComposeController, fix postgres location search. Closes #4242 and #4239 2023-03-29 02:13:54 -06:00
daniel 4d9189e87c
Merge pull request #4262 from pixelfed/staging
Update ApiV1Controller, fix blocking remote accounts. Closes #4256
2023-03-28 20:23:28 -06:00
Daniel Supernault ccd82032df
Update changelog 2023-03-28 20:23:03 -06:00
Daniel Supernault 8e71e0c03e
Update ApiV1Controller, fix blocking remote accounts. Closes #4256 2023-03-28 20:21:54 -06:00
Shlee 031290d987
Update NotificationCard.vue 2022-12-12 00:31:34 +10:30
Shlee 312bc06685
Update NotificationCard.vue 2022-12-11 15:09:29 +10:30
Nils van Lück c96bcd559d Check imported emojis for mimetype
Signed-off-by: Nils van Lück <nils@vanlueck.dev>
2022-12-08 21:10:10 +01:00
Nils van Lück af28aecf21 Add a command to import emoji archives 2022-12-04 22:54:27 +01:00
781 zmienionych plików z 79942 dodań i 29606 usunięć

Wyświetl plik

@ -7,7 +7,7 @@ jobs:
build:
docker:
# Specify the version you desire here
- image: cimg/php:8.1.12
- image: cimg/php:8.2.5
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
@ -21,7 +21,12 @@ jobs:
steps:
- checkout
- run: sudo apt update && sudo apt install zlib1g-dev libsqlite3-dev
- run:
name: "Create Environment file and generate app key"
command: |
mv .env.testing .env
- run: sudo apt install zlib1g-dev libsqlite3-dev
# Download and cache dependencies
@ -29,25 +34,24 @@ jobs:
- restore_cache:
keys:
# "composer.lock" can be used if it is committed to the repo
- v1-dependencies-{{ checksum "composer.json" }}
- v2-dependencies-{{ checksum "composer.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- v2-dependencies-
- run: composer install -n --prefer-dist
- save_cache:
key: composer-v1-{{ checksum "composer.lock" }}
key: v2-dependencies-{{ checksum "composer.json" }}
paths:
- vendor
- run: cp .env.testing .env
- run: php artisan config:cache
- run: php artisan route:clear
- run: php artisan storage:link
- run: php artisan key:generate
- run: php artisan config:clear
# run tests with phpunit or codecept
- run: ./vendor/bin/phpunit
- run: php artisan test
- store_test_results:
path: tests/_output
- store_artifacts:

Wyświetl plik

@ -4,4 +4,4 @@
## Usage: redis-cli [flags] [args]
## Example: "redis-cli KEYS *" or "ddev redis-cli INFO" or "ddev redis-cli --version"
redis-cli -p 6379 -h redis $@
exec redis-cli -p 6379 -h redis "$@"

Wyświetl plik

@ -1,8 +1,30 @@
data
Dockerfile
contrib/docker/Dockerfile.*
docker-compose*.yml
.dockerignore
.git
.gitignore
.env
.DS_Store
/.bash_history
/.bash_profile
/.bashrc
/.composer
/.env
/.env.dottie-backup
/.git
/.git-credentials
/.gitconfig
/.gitignore
/.idea
/.vagrant
/bootstrap/cache
/docker-compose-state/
/Homestead.json
/Homestead.yaml
/node_modules
/npm-debug.log
/public/hot
/public/storage
/public/vendor/horizon
/storage/*.key
/storage/docker
/vendor
/yarn-error.log
# Exceptions - these *MUST* be last
!/bootstrap/cache/.gitignore
!/public/vendor/horizon/.gitignore

Wyświetl plik

@ -1,9 +1,27 @@
root = true
[*]
indent_style = space
indent_size = 4
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.{sh,envsh,env,env*}]
indent_style = space
indent_size = 4
# ShellCheck config
shell_variant = bash # like -ln=bash
binary_next_line = true # like -bn
switch_case_indent = true # like -ci
space_redirects = false # like -sr
keep_padding = false # like -kp
function_next_line = true # like -fn
never_split = true # like -ns
simplify = true

Plik diff jest za duży Load Diff

Wyświetl plik

@ -8,6 +8,7 @@ OPEN_REGISTRATION="false"
ENFORCE_EMAIL_VERIFICATION="false"
PF_MAX_USERS="1000"
OAUTH_ENABLED="true"
ENABLE_CONFIG_CACHE=true
# Media Configuration
PF_OPTIMIZE_IMAGES="true"
@ -68,7 +69,6 @@ MAIL_FROM_NAME="Pixelfed"
## S3 Configuration (Post-Installer)
PF_ENABLE_CLOUD=false
FILESYSTEM_DRIVER=local
FILESYSTEM_CLOUD=s3
#AWS_ACCESS_KEY_ID=
#AWS_SECRET_ACCESS_KEY=

Wyświetl plik

@ -1,3 +1,5 @@
# shellcheck disable=SC2034,SC2148
APP_NAME="Pixelfed Test"
APP_ENV=local
APP_KEY=base64:lwX95GbNWX3XsucdMe0XwtOKECta3h/B+p9NbH2jd0E=
@ -62,6 +64,8 @@ CS_BLOCKED_DOMAINS='example.org,example.net,example.com'
CS_CW_DOMAINS='example.org,example.net,example.com'
CS_UNLISTED_DOMAINS='example.org,example.net,example.com'
## Optional
## Optional
#HORIZON_DARKMODE=false # Horizon theme darkmode
#HORIZON_EMBED=false # Single Docker Container mode
#HORIZON_EMBED=false # Single Docker Container mode
ENABLE_CONFIG_CACHE=false

7
.gitattributes vendored
Wyświetl plik

@ -3,3 +3,10 @@
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
# Collapse diffs for generated files:
public/**/*.js text -diff
public/**/*.json text -diff
public/**/*.css text -diff
public/img/* binary -diff
public/fonts/* binary -diff

Wyświetl plik

@ -1,125 +0,0 @@
---
name: Build Docker image
on:
workflow_dispatch:
push:
branches:
- dev
tags:
- '*'
pull_request:
paths:
- .github/workflows/build-docker.yml
- contrib/docker/Dockerfile.apache
- contrib/docker/Dockerfile.fpm
permissions:
contents: read
jobs:
build-docker-apache:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Docker Lint
uses: hadolint/hadolint-action@v3.0.0
with:
dockerfile: contrib/docker/Dockerfile.apache
failure-threshold: error
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
secrets: inherit
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
if: github.event_name != 'pull_request'
- name: Fetch tags
uses: docker/metadata-action@v4
secrets: inherit
id: meta
with:
images: ${{ secrets.DOCKER_HUB_ORGANISATION }}/pixelfed
flavor: |
latest=auto
suffix=-apache
tags: |
type=edge,branch=dev
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
type=ref,event=pr
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: contrib/docker/Dockerfile.apache
platforms: linux/amd64,linux/arm64
builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-docker-fpm:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Docker Lint
uses: hadolint/hadolint-action@v3.0.0
with:
dockerfile: contrib/docker/Dockerfile.fpm
failure-threshold: error
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
secrets: inherit
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
if: github.event_name != 'pull_request'
- name: Fetch tags
uses: docker/metadata-action@v4
secrets: inherit
id: meta
with:
images: ${{ secrets.DOCKER_HUB_ORGANISATION }}/pixelfed
flavor: |
suffix=-fpm
tags: |
type=edge,branch=dev
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
type=ref,event=pr
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: contrib/docker/Dockerfile.fpm
platforms: linux/amd64,linux/arm64
builder: ${{ steps.buildx.outputs.name }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max

230
.github/workflows/docker.yml vendored 100644
Wyświetl plik

@ -0,0 +1,230 @@
---
name: Docker
on:
# See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
workflow_dispatch:
# See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
push:
branches:
- dev
- staging
tags:
- "*"
# See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
pull_request:
types:
- opened
- reopened
- synchronize
jobs:
lint:
name: hadolint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Docker Lint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
failure-threshold: error
shellcheck:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
SHELLCHECK_OPTS: --shell=bash --external-sources
with:
version: v0.9.0
additional_files: "*.envsh .env .env.docker .env.example .env.testing"
bats:
name: Bats Testing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run bats
run: docker run -v "$PWD:/var/www" bats/bats:latest /var/www/tests/bats
build:
name: Build, Test, and Push
runs-on: ubuntu-latest
strategy:
fail-fast: false
# See: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs
matrix:
php_version:
- 8.2
- 8.3
target_runtime:
- apache
- fpm
- nginx
php_base:
- apache
- fpm
# See: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#excluding-matrix-configurations
# See: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixexclude
exclude:
# targeting [apache] runtime with [fpm] base type doesn't make sense
- target_runtime: apache
php_base: fpm
# targeting [fpm] runtime with [apache] base type doesn't make sense
- target_runtime: fpm
php_base: apache
# targeting [nginx] runtime with [apache] base type doesn't make sense
- target_runtime: nginx
php_base: apache
# See: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-concurrency-and-the-default-behavior
concurrency:
group: docker-build-${{ github.ref }}-${{ matrix.php_base }}-${{ matrix.php_version }}-${{ matrix.target_runtime }}
cancel-in-progress: true
permissions:
contents: read
packages: write
env:
# Set the repo variable [DOCKER_HUB_USERNAME] to override the default
# at https://github.com/<user>/<project>/settings/variables/actions
DOCKER_HUB_USERNAME: ${{ vars.DOCKER_HUB_USERNAME || 'pixelfed' }}
# Set the repo variable [DOCKER_HUB_ORGANISATION] to override the default
# at https://github.com/<user>/<project>/settings/variables/actions
DOCKER_HUB_ORGANISATION: ${{ vars.DOCKER_HUB_ORGANISATION || 'pixelfed' }}
# Set the repo variable [DOCKER_HUB_REPO] to override the default
# at https://github.com/<user>/<project>/settings/variables/actions
DOCKER_HUB_REPO: ${{ vars.DOCKER_HUB_REPO || 'pixelfed' }}
# For Docker Hub pushing to work, you need the secret [DOCKER_HUB_TOKEN]
# set to your Personal Access Token at https://github.com/<user>/<project>/settings/secrets/actions
#
# ! NOTE: no [login] or [push] will happen to Docker Hub until this secret is set!
HAS_DOCKER_HUB_CONFIGURED: ${{ secrets.DOCKER_HUB_TOKEN != '' }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
id: buildx
with:
version: v0.12.0 # *or* newer, needed for annotations to work
# See: https://github.com/docker/login-action?tab=readme-ov-file#github-container-registry
- name: Log in to the GitHub Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# See: https://github.com/docker/login-action?tab=readme-ov-file#docker-hub
- name: Login to Docker Hub registry (conditionally)
if: ${{ env.HAS_DOCKER_HUB_CONFIGURED == true }}
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Docker meta
uses: docker/metadata-action@v5
id: meta
with:
images: |
name=ghcr.io/${{ github.repository }},enable=true
name=${{ env.DOCKER_HUB_ORGANISATION }}/${{ env.DOCKER_HUB_REPO }},enable=${{ env.HAS_DOCKER_HUB_CONFIGURED }}
flavor: |
latest=auto
suffix=-${{ matrix.target_runtime }}-${{ matrix.php_version }}
tags: |
type=raw,value=dev,enable=${{ github.ref == format('refs/heads/{0}', 'dev') }}
type=raw,value=staging,enable=${{ github.ref == format('refs/heads/{0}', 'staging') }}
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
type=ref,event=branch,prefix=branch-
type=ref,event=pr,prefix=pr-
type=ref,event=tag
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
- name: Docker meta (Cache)
uses: docker/metadata-action@v5
id: cache
with:
images: |
name=ghcr.io/${{ github.repository }}-cache,enable=true
name=${{ env.DOCKER_HUB_ORGANISATION }}/${{ env.DOCKER_HUB_REPO }}-cache,enable=${{ env.HAS_DOCKER_HUB_CONFIGURED }}
flavor: |
latest=auto
suffix=-${{ matrix.target_runtime }}-${{ matrix.php_version }}
tags: |
type=raw,value=dev,enable=${{ github.ref == format('refs/heads/{0}', 'dev') }}
type=raw,value=staging,enable=${{ github.ref == format('refs/heads/{0}', 'staging') }}
type=pep440,pattern={{raw}}
type=pep440,pattern=v{{major}}.{{minor}}
type=ref,event=branch,prefix=branch-
type=ref,event=pr,prefix=pr-
type=ref,event=tag
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
target: ${{ matrix.target_runtime }}-runtime
platforms: linux/amd64,linux/arm64
builder: ${{ steps.buildx.outputs.name }}
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
push: true
sbom: true
provenance: true
build-args: |
PHP_VERSION=${{ matrix.php_version }}
PHP_BASE_TYPE=${{ matrix.php_base }}
cache-from: |
type=gha,scope=${{ matrix.target_runtime }}-${{ matrix.php_base }}-${{ matrix.php_version }}
cache-to: |
type=gha,mode=max,scope=${{ matrix.target_runtime }}-${{ matrix.php_base }}-${{ matrix.php_version }}
${{ steps.cache.outputs.tags }}
# goss validate the image
#
# See: https://github.com/goss-org/goss
- uses: e1himself/goss-installation-action@v1
with:
version: "v0.4.4"
- name: Execute Goss tests
run: |
dgoss run \
-v "./.env.testing:/var/www/.env" \
-e "EXPECTED_PHP_VERSION=${{ matrix.php_version }}" \
-e "PHP_BASE_TYPE=${{ matrix.php_base }}" \
${{ steps.meta.outputs.tags }}

43
.gitignore vendored
Wyświetl plik

@ -1,22 +1,31 @@
.DS_Store
/.bash_history
/.bash_profile
/.bashrc
/.composer
/.env
/.env.dottie-backup
#/.git
/.git-credentials
/.gitconfig
#/.gitignore
/.idea
/.vagrant
/bootstrap/cache
/docker-compose-state/
/Homestead.json
/Homestead.yaml
/node_modules
/npm-debug.log
/public/hot
/public/storage
/public/vendor/horizon
/storage/*.key
/storage/docker
/vendor
/.idea
/.vscode
/.vagrant
/docker-volumes
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
.DS_Store
.bash_profile
.bash_history
.bashrc
.gitconfig
.git-credentials
/.composer/
/nginx.conf
/yarn-error.log
/public/build
# Exceptions - these *MUST* be last
!/bootstrap/cache/.gitignore
!/public/vendor/horizon/.gitignore

5
.hadolint.yaml 100644
Wyświetl plik

@ -0,0 +1,5 @@
ignored:
- DL3002 # warning: Last USER should not be root
- DL3008 # warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
- SC2046 # warning: Quote this to prevent word splitting.
- SC2086 # info: Double quote to prevent globbing and word splitting.

Wyświetl plik

@ -0,0 +1,4 @@
{
"MD013": false,
"MD014": false
}

12
.shellcheckrc 100644
Wyświetl plik

@ -0,0 +1,12 @@
# See: https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md#rc-files
source-path=SCRIPTDIR
# Allow opening any 'source'd file, even if not specified as input
external-sources=true
# Turn on warnings for unquoted variables with safe values
enable=quote-safe-variables
# Turn on warnings for unassigned uppercase variables
enable=check-unassigned-uppercase

14
.vscode/extensions.json vendored 100644
Wyświetl plik

@ -0,0 +1,14 @@
{
"recommendations": [
"foxundermoon.shell-format",
"timonwong.shellcheck",
"jetmartin.bats",
"aaron-bond.better-comments",
"streetsidesoftware.code-spell-checker",
"editorconfig.editorconfig",
"github.vscode-github-actions",
"bmewburn.vscode-intelephense-client",
"redhat.vscode-yaml",
"ms-azuretools.vscode-docker"
]
}

21
.vscode/settings.json vendored 100644
Wyświetl plik

@ -0,0 +1,21 @@
{
"shellformat.useEditorConfig": true,
"[shellscript]": {
"files.eol": "\n",
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml"
},
"[dockercompose]": {
"editor.defaultFormatter": "redhat.vscode-yaml",
"editor.autoIndent": "advanced",
},
"yaml.schemas": {
"https://json.schemastore.org/composer": "https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json"
},
"files.associations": {
".env": "shellscript",
".env.*": "shellscript"
}
}

Wyświetl plik

@ -1,7 +1,427 @@
# Release Notes
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.5...dev)
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.12.1...dev)
### Updates
- Update DirectMessageController, add 72 hour delay for new accounts before they can send a DM ([61d105fd](https://github.com/pixelfed/pixelfed/commit/61d105fd))
- Update AdminCuratedRegisterController, increase message length from 1000 to 3000 ([9a5e3471](https://github.com/pixelfed/pixelfed/commit/))
- ([](https://github.com/pixelfed/pixelfed/commit/9a5e3471))
## [v0.12.1 (2024-05-07)](https://github.com/pixelfed/pixelfed/compare/v0.12.0...v0.12.1)
### Updates
- Update ApiV1Dot1Controller, fix in app registration bug that prevents proper auth flow due to missing oauth scopes ([cbf996c9](https://github.com/pixelfed/pixelfed/commit/cbf996c9))
- Update ConfigCacheService, fix database race condition and fallback to file config and enable by default ([60a62b59](https://github.com/pixelfed/pixelfed/commit/60a62b59))
## [v0.12.0 (2024-04-29)](https://github.com/pixelfed/pixelfed/compare/v0.11.13...v0.12.0)
### Updates
- Update SoftwareUpdateService, add command to refresh latest versions ([632f2cb6](https://github.com/pixelfed/pixelfed/commit/632f2cb6))
- Update Post.vue, fix cache bug ([3a27e637](https://github.com/pixelfed/pixelfed/commit/3a27e637))
- Update StatusHashtagService, use more efficient cached count ([592c8412](https://github.com/pixelfed/pixelfed/commit/592c8412))
- Update DiscoverController, handle discover hashtag redirects ([18382e8a](https://github.com/pixelfed/pixelfed/commit/18382e8a))
- Update ApiV1Controller, use admin filter service ([94503a1c](https://github.com/pixelfed/pixelfed/commit/94503a1c))
- Update SearchApiV2Service, use more efficient query ([cee618e8](https://github.com/pixelfed/pixelfed/commit/cee618e8))
- Update Curated Onboarding view, fix concierge form ([15ad69f7](https://github.com/pixelfed/pixelfed/commit/15ad69f7))
- Update AP Profile Transformer, add `suspended` attribute ([25f3fa06](https://github.com/pixelfed/pixelfed/commit/25f3fa06))
- Update AP Profile Transformer, fix movedTo attribute ([63100fe9](https://github.com/pixelfed/pixelfed/commit/63100fe9))
- Update AP Profile Transformer, fix suspended attributes ([2e5e68e4](https://github.com/pixelfed/pixelfed/commit/2e5e68e4))
- Update PrivacySettings controller, add cache invalidation ([e742d595](https://github.com/pixelfed/pixelfed/commit/e742d595))
- Update ProfileController, preserve deleted actor objects for federated account deletion and use more efficient account cache lookup ([853a729f](https://github.com/pixelfed/pixelfed/commit/853a729f))
- Update SiteController, add curatedOnboarding method that gracefully falls back to open registration when applicable ([95199843](https://github.com/pixelfed/pixelfed/commit/95199843))
- Update AP transformers, add DeleteActor activity ([bcce1df6](https://github.com/pixelfed/pixelfed/commit/bcce1df6))
- Update commands, add user account delete cli command to federate account deletion ([4aa0e25f](https://github.com/pixelfed/pixelfed/commit/4aa0e25f))
- Update web-api popular accounts route to its own method to remove the breaking oauth scope bug ([a4bc5ce3](https://github.com/pixelfed/pixelfed/commit/a4bc5ce3))
- Update config cache ([5e4d4eff](https://github.com/pixelfed/pixelfed/commit/5e4d4eff))
- Update Config, use config_cache ([7785a2da](https://github.com/pixelfed/pixelfed/commit/7785a2da))
- Update ApiV1Dot1Controller, use config_cache for in-app registration ([b0cb4456](https://github.com/pixelfed/pixelfed/commit/b0cb4456))
- Update captcha, use config_cache helper ([8a89e3c9](https://github.com/pixelfed/pixelfed/commit/8a89e3c9))
- Update custom emoji, add config_cache support ([481314cd](https://github.com/pixelfed/pixelfed/commit/481314cd))
- Update ProfileController, fix permalink redirect bug ([75081e60](https://github.com/pixelfed/pixelfed/commit/75081e60))
- Update admin css, use font-display:swap for nucleo icons ([8a0c456e](https://github.com/pixelfed/pixelfed/commit/8a0c456e))
- Update PixelfedDirectoryController, fix boolean cast bug ([f08aab22](https://github.com/pixelfed/pixelfed/commit/f08aab22))
- Update PixelfedDirectoryController, use cached stats ([f2f2a809](https://github.com/pixelfed/pixelfed/commit/f2f2a809))
- Update AdminDirectoryController, fix type casting ([ad506e90](https://github.com/pixelfed/pixelfed/commit/ad506e90))
- Update image pipeline, use config_cache ([a72188a7](https://github.com/pixelfed/pixelfed/commit/a72188a7))
- Update cloud storage, use config_cache ([665581d8](https://github.com/pixelfed/pixelfed/commit/665581d8))
- Update pixelfed.max_album_length, use config_cache ([fecbe189](https://github.com/pixelfed/pixelfed/commit/fecbe189))
- Update media_types, use config_cache ([d670de17](https://github.com/pixelfed/pixelfed/commit/d670de17))
- Update landing settings, use config_cache ([40478f25](https://github.com/pixelfed/pixelfed/commit/40478f25))
- Update activitypub setting, use config_cache ([5071aaf4](https://github.com/pixelfed/pixelfed/commit/5071aaf4))
- Update oauth setting, use config_cache ([ce228f7f](https://github.com/pixelfed/pixelfed/commit/ce228f7f))
- Update stories config, use config_cache ([d1adb109](https://github.com/pixelfed/pixelfed/commit/d1adb109))
- Update ig import, use config_cache ([da0e0ffa](https://github.com/pixelfed/pixelfed/commit/da0e0ffa))
- Update autospam config, use config_cache ([a76cb5f4](https://github.com/pixelfed/pixelfed/commit/a76cb5f4))
- Update app.name config, use config_cache ([911446c0](https://github.com/pixelfed/pixelfed/commit/911446c0))
- Update UserObserver, fix type casting ([949e9979](https://github.com/pixelfed/pixelfed/commit/949e9979))
- Update user_filters, use config_cache ([6ce513f8](https://github.com/pixelfed/pixelfed/commit/6ce513f8))
- Update filesystems config, add to config_cache ([087b2791](https://github.com/pixelfed/pixelfed/commit/087b2791))
- Update web-admin routes, add setting api routes ([828a456f](https://github.com/pixelfed/pixelfed/commit/828a456f))
- Update hashtag component ([cee979ed](https://github.com/pixelfed/pixelfed/commit/cee979ed))
- Update AdminReadMore component, add .prevent to click action ([704e7b12](https://github.com/pixelfed/pixelfed/commit/704e7b12))
- Update admin dashboard, add admin settings partials ([eb487123](https://github.com/pixelfed/pixelfed/commit/eb487123))
- Update admin settings, refactor to vue component ([674e560f](https://github.com/pixelfed/pixelfed/commit/674e560f))
- Update ConfigCacheService, encrypt keys at rest ([3628b462](https://github.com/pixelfed/pixelfed/commit/3628b462))
- Update RemoteFollowImportRecent, use MediaPathService ([5162c070](https://github.com/pixelfed/pixelfed/commit/5162c070))
- Update AdminSettingsController, add user filter max limit settings ([ac1f0748](https://github.com/pixelfed/pixelfed/commit/ac1f0748))
- Update AdminSettingsController, add AdminSettingsService ([dcc5f416](https://github.com/pixelfed/pixelfed/commit/dcc5f416))
- Update AdminSettings component, fix user settings ([aba1e13d](https://github.com/pixelfed/pixelfed/commit/aba1e13d))
- Update AdminInstances component ([ec2fdd61](https://github.com/pixelfed/pixelfed/commit/ec2fdd61))
- Update AdminSettings, add max_account_size support ([2dcbc1d5](https://github.com/pixelfed/pixelfed/commit/2dcbc1d5))
- Update AdminSettings, use better validation for user integer settings ([d946afcc](https://github.com/pixelfed/pixelfed/commit/d946afcc))
- Update spa sass, fix timestamp dark mode bug ([4147f7c5](https://github.com/pixelfed/pixelfed/commit/4147f7c5))
- Update relationships view, fix unfollow hashtag bug. Fixes #5008 ([8c693640](https://github.com/pixelfed/pixelfed/commit/8c693640))
- Update PrivacySettings controller, refresh RelationshipService when unmute/unblocking ([b7322b68](https://github.com/pixelfed/pixelfed/commit/b7322b68))
- Update ApiV1Controller, improve refresh relations logic when (un)muting or (un)blocking ([b8e96a5f](https://github.com/pixelfed/pixelfed/commit/b8e96a5f))
- Update context menu, add mute/block/unfollow actions and update relationship store accordingly ([81d1e0fd](https://github.com/pixelfed/pixelfed/commit/81d1e0fd))
- Update docker env, fix config_cache. Fixes #5033 ([858fcbf6](https://github.com/pixelfed/pixelfed/commit/858fcbf6))
- Update UnfollowPipeline, fix follower count cache bug ([6bdf73de](https://github.com/pixelfed/pixelfed/commit/6bdf73de))
- Update VideoPresenter component, add webkit-playsinline attribute to video element to prevent the full screen video player ([ad032916](https://github.com/pixelfed/pixelfed/commit/ad032916))
- Update VideoPlayer component, add playsinline attribute to video element ([8af23607](https://github.com/pixelfed/pixelfed/commit/8af23607))
- Update StatusController, refactor status embeds ([9a7acc12](https://github.com/pixelfed/pixelfed/commit/9a7acc12))
- Update ProfileController, refactor profile embeds ([8b8b1ffc](https://github.com/pixelfed/pixelfed/commit/8b8b1ffc))
- Update profile embed view, fix height bug ([65166570](https://github.com/pixelfed/pixelfed/commit/65166570))
- Update CustomEmojiService, only return local emoji ([7f8bba44](https://github.com/pixelfed/pixelfed/commit/7f8bba44))
- Update Like model, increase max likes per day from 500 to 1500 ([4223119f](https://github.com/pixelfed/pixelfed/commit/4223119f))
## [v0.11.13 (2024-03-05)](https://github.com/pixelfed/pixelfed/compare/v0.11.12...v0.11.13)
### Features
- Account Migrations ([#4968](https://github.com/pixelfed/pixelfed/pull/4968)) ([4a6be6212](https://github.com/pixelfed/pixelfed/pull/4968/commits/4a6be6212))
- Curated Onboarding ([#4946](https://github.com/pixelfed/pixelfed/pull/4946)) ([8dac2caf](https://github.com/pixelfed/pixelfed/commit/8dac2caf))
- Add Curated Onboarding Templates ([071163b4](https://github.com/pixelfed/pixelfed/commit/071163b4))
- Add Remote Reports to Admin Dashboard Reports page ([ef0ff78e](https://github.com/pixelfed/pixelfed/commit/ef0ff78e))
- Improved Docker Support ([#4844](https://github.com/pixelfed/pixelfed/pull/4844)) ([d92cf7f](https://github.com/pixelfed/pixelfed/commit/d92cf7f))
### Updates
- Update Inbox, cast live filters to lowercase ([d835e0ad](https://github.com/pixelfed/pixelfed/commit/d835e0ad))
- Update federation config, increase default timeline days falloff to 90 days from 2 days. Fixes #4905 ([011834f4](https://github.com/pixelfed/pixelfed/commit/011834f4))
- Update cache config, use predis as default redis driver client ([ea6b1623](https://github.com/pixelfed/pixelfed/commit/ea6b1623))
- Update .gitattributes to collapse diffs on generated files ([ThisIsMissEm](https://github.com/pixelfed/pixelfed/commit/9978b2b9))
- Update api v1/v2 instance endpoints, bump mastoapi version from 2.7.2 to 3.5.3 ([545f7d5e](https://github.com/pixelfed/pixelfed/commit/545f7d5e))
- Update ApiV1Controller, implement better limit logic to gracefully handle requests with limits that exceed the max ([1f74a95d](https://github.com/pixelfed/pixelfed/commit/1f74a95d))
- Update AdminCuratedRegisterController, show oldest applications first ([c4dde641](https://github.com/pixelfed/pixelfed/commit/c4dde641))
- Update Directory logic, add curated onboarding support ([59c70239](https://github.com/pixelfed/pixelfed/commit/59c70239))
- Update Inbox and StatusObserver, fix silently rejected direct messages due to saveQuietly which failed to generate a snowflake id ([089ba3c4](https://github.com/pixelfed/pixelfed/commit/089ba3c4))
- Update Curated Onboarding dashboard, improve application filtering and make it easier to distinguish response state ([2b5d7235](https://github.com/pixelfed/pixelfed/commit/2b5d7235))
- Update AdminReports, add story reports and fix cs ([767522a8](https://github.com/pixelfed/pixelfed/commit/767522a8))
- Update AdminReportController, add story report support ([a16309ac](https://github.com/pixelfed/pixelfed/commit/a16309ac))
- Update kb, add email confirmation issues page ([2f48df8c](https://github.com/pixelfed/pixelfed/commit/2f48df8c))
- Update AdminCuratedRegisterController, filter confirmation activities from activitylog ([ab9ecb6e](https://github.com/pixelfed/pixelfed/commit/ab9ecb6e))
- Update Inbox, fix flag validation condition, allow profile reports ([402a4607](https://github.com/pixelfed/pixelfed/commit/402a4607))
- Update AccountTransformer, fix follower/following count visibility bug ([542d1106](https://github.com/pixelfed/pixelfed/commit/542d1106))
- Update ProfileMigration model, add target relation ([3f053997](https://github.com/pixelfed/pixelfed/commit/3f053997))
- Update ApiV1Controller, update Notifications endpoint to filter notifications with missing activities ([a933615b](https://github.com/pixelfed/pixelfed/commit/a933615b))
- Update ApiV1Controller, fix public timeline scope, properly support both local + remote parameters ([d6eac655](https://github.com/pixelfed/pixelfed/commit/d6eac655))
- Update ApiV1Controller, handle public feed parameter bug to gracefully fallback to min_id=1 when max_id=0 ([e3826c58](https://github.com/pixelfed/pixelfed/commit/e3826c58))
- Update ApiV1Controller, fix hashtag feed to include private posts from accounts you follow or your own, and your own unlisted posts ([3b5500b3](https://github.com/pixelfed/pixelfed/commit/3b5500b3))
- Update checkpoint view, improve input autocomplete. Fixes ([#4959](https://github.com/pixelfed/pixelfed/pull/4959)) ([d18824e7](https://github.com/pixelfed/pixelfed/commit/d18824e7))
- Update navbar.vue, removes the 50px limit ([#4969](https://github.com/pixelfed/pixelfed/pull/4969)) ([7fd5599](https://github.com/pixelfed/pixelfed/commit/7fd5599))
- Update ComposeModal.vue, add an informative UI error message when trying to create a mixed media album ([#4886](https://github.com/pixelfed/pixelfed/pull/4886)) ([fd4f41a](https://github.com/pixelfed/pixelfed/commit/fd4f41a))
## [v0.11.12 (2024-02-16)](https://github.com/pixelfed/pixelfed/compare/v0.11.11...v0.11.12)
### Features
- Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a))
- Added Software Update banner to admin home feeds ([b0fb1988](https://github.com/pixelfed/pixelfed/commit/b0fb1988))
### Updates
- Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3))
- Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc))
- Update Federation, use proper Content-Type headers for following/follower collections ([fb0bb9a3](https://github.com/pixelfed/pixelfed/commit/fb0bb9a3))
- Update ActivityPubFetchService, enforce stricter Content-Type validation ([1232cfc8](https://github.com/pixelfed/pixelfed/commit/1232cfc8))
- Update status view, fix unlisted/private scope bug ([0f3ca194](https://github.com/pixelfed/pixelfed/commit/0f3ca194))
## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11)
### Fixes
- Fix api endpoints ([fd7f5dbb](https://github.com/pixelfed/pixelfed/commit/fd7f5dbb))
## [v0.11.10 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.9...v0.11.10)
### Added
- Resilient Media Storage ([#4665](https://github.com/pixelfed/pixelfed/pull/4665)) ([fb1deb6](https://github.com/pixelfed/pixelfed/commit/fb1deb6))
- Video WebP2P ([#4713](https://github.com/pixelfed/pixelfed/pull/4713)) ([0405ef12](https://github.com/pixelfed/pixelfed/commit/0405ef12))
- Added user:2fa command to easily disable 2FA for given account ([c6408fd7](https://github.com/pixelfed/pixelfed/commit/c6408fd7))
- Added `avatar:storage-deep-clean` command to dispatch remote avatar storage cleanup jobs ([c37b7cde](https://github.com/pixelfed/pixelfed/commit/c37b7cde))
- Added S3 command to rewrite media urls ([5b3a5610](https://github.com/pixelfed/pixelfed/commit/5b3a5610))
- Experimental home feed ([#4752](https://github.com/pixelfed/pixelfed/pull/4752)) ([c39b9afb](https://github.com/pixelfed/pixelfed/commit/c39b9afb))
- Added `app:hashtag-cached-count-update` command to update cached_count of hashtags and add to scheduler to run every 25 minutes past the hour ([1e31fee6](https://github.com/pixelfed/pixelfed/commit/1e31fee6))
- Added `app:hashtag-related-generate` command to generate related hashtags ([176b4ed7](https://github.com/pixelfed/pixelfed/commit/176b4ed7))
- Added Mutual Followers API endpoint ([33dbbe46](https://github.com/pixelfed/pixelfed/commit/33dbbe46))
- Added User Domain Blocks ([#4834](https://github.com/pixelfed/pixelfed/pull/4834)) ([fa0380ac](https://github.com/pixelfed/pixelfed/commit/fa0380ac))
- Added Parental Controls ([#4862](https://github.com/pixelfed/pixelfed/pull/4862)) ([c91f1c59](https://github.com/pixelfed/pixelfed/commit/c91f1c59))
- Added Forgot Email Feature ([67c650b1](https://github.com/pixelfed/pixelfed/commit/67c650b1))
- Added S3 IG Import Media Storage support ([#4891](https://github.com/pixelfed/pixelfed/pull/4891)) ([081360b9](https://github.com/pixelfed/pixelfed/commit/081360b9))
### Federation
- Update Privacy Settings, add support for Mastodon `indexable` search flag ([fc24630e](https://github.com/pixelfed/pixelfed/commit/fc24630e))
- Update AP Helpers, consume actor `indexable` attribute ([fbdcdd9d](https://github.com/pixelfed/pixelfed/commit/fbdcdd9d))
### Updates
- Update FollowerService, add forget method to RelationshipService call to reduce load when mass purging ([347e4f59](https://github.com/pixelfed/pixelfed/commit/347e4f59))
- Update FollowServiceWarmCache, improve handling larger following/follower lists ([61a6d904](https://github.com/pixelfed/pixelfed/commit/61a6d904))
- Update StoryApiV1Controller, add viewers route to view story viewers ([941736ce](https://github.com/pixelfed/pixelfed/commit/941736ce))
- Update NotificationService, improve cache warming query ([2496386d](https://github.com/pixelfed/pixelfed/commit/2496386d))
- Update StatusService, hydrate accounts on request instead of caching them along with status objects ([223661ec](https://github.com/pixelfed/pixelfed/commit/223661ec))
- Update profile embed, fix resize ([dc23c21d](https://github.com/pixelfed/pixelfed/commit/dc23c21d))
- Update Status model, improve thumb logic ([d969a973](https://github.com/pixelfed/pixelfed/commit/d969a973))
- Update Status model, allow unlisted thumbnails ([1f0a45b7](https://github.com/pixelfed/pixelfed/commit/1f0a45b7))
- Update StatusTagsPipeline, fix object tags and slug normalization ([d295e605](https://github.com/pixelfed/pixelfed/commit/d295e605))
- Update Note and CreateNote transformers, include attachment blurhash, width and height ([ce1afe27](https://github.com/pixelfed/pixelfed/commit/ce1afe27))
- Update ap helpers, store media attachment width and height if present ([8c969191](https://github.com/pixelfed/pixelfed/commit/8c969191))
- Update Sign-in with Mastodon, allow usage when registrations are closed ([895dc4fa](https://github.com/pixelfed/pixelfed/commit/895dc4fa))
- Update profile embeds, filter sensitive posts ([ede5ec3b](https://github.com/pixelfed/pixelfed/commit/ede5ec3b))
- Update ApiV1Controller, hydrate reblog interactions. Fixes ([#4686](https://github.com/pixelfed/pixelfed/issues/4686)) ([135798eb](https://github.com/pixelfed/pixelfed/commit/135798eb))
- Update AdminReportController, add `profile_id` to group by. Fixes ([#4685](https://github.com/pixelfed/pixelfed/issues/4685)) ([e4d3b196](https://github.com/pixelfed/pixelfed/commit/e4d3b196))
- Update user:admin command, improve logic. Fixes ([#2465](https://github.com/pixelfed/pixelfed/issues/2465)) ([01bac511](https://github.com/pixelfed/pixelfed/commit/01bac511))
- Update AP helpers, adjust RemoteAvatarFetch ttl from 24h to 3 months ([36b23fe3](https://github.com/pixelfed/pixelfed/commit/36b23fe3))
- Update AvatarPipeline, improve refresh logic and garbage collection to purge old avatars ([82798b5e](https://github.com/pixelfed/pixelfed/commit/82798b5e))
- Update CreateAvatar job, add processing constraints and set `is_remote` attribute ([319ced40](https://github.com/pixelfed/pixelfed/commit/319ced40))
- Update RemoteStatusDelete and DecrementPostCount pipelines ([edbcf3ed](https://github.com/pixelfed/pixelfed/commit/edbcf3ed))
- Update lexer regex, fix mention regex and add more tests ([778e83d3](https://github.com/pixelfed/pixelfed/commit/778e83d3))
- Update StatusTransformer, generate autolink on request ([dfe2379b](https://github.com/pixelfed/pixelfed/commit/dfe2379b))
- Update ComposeModal component, fix multi filter bug and allow media re-ordering before upload/posting ([56e315f6](https://github.com/pixelfed/pixelfed/commit/56e315f6))
- Update ApiV1Dot1Controller, allow iar rate limits to be configurable ([28a80803](https://github.com/pixelfed/pixelfed/commit/28a80803))
- Update ApiV1Dot1Controller, add domain to iar redirect ([1f82d47c](https://github.com/pixelfed/pixelfed/commit/1f82d47c))
- Update ApiV1Dot1Controller, add configurable app confirm rate limit ttl ([4c6a0719](https://github.com/pixelfed/pixelfed/commit/4c6a0719))
- Update LikePipeline, dispatch to feed queue. Fixes ([#4723](https://github.com/pixelfed/pixelfed/issues/4723)) ([da510089](https://github.com/pixelfed/pixelfed/commit/da510089))
- Update AccountImport ([5a2d7e3e](https://github.com/pixelfed/pixelfed/commit/5a2d7e3e))
- Update ImportPostController, fix IG bug with missing spaces between hashtags ([9c24157a](https://github.com/pixelfed/pixelfed/commit/9c24157a))
- Update ApiV1Controller, fix mutes in home feed ([ddc21714](https://github.com/pixelfed/pixelfed/commit/ddc21714))
- Update AP helpers, improve preferredUsername validation ([21218c79](https://github.com/pixelfed/pixelfed/commit/21218c79))
- Update delete pipelines, properly invoke StatusHashtag delete events ([ce54d29c](https://github.com/pixelfed/pixelfed/commit/ce54d29c))
- Update mail config ([0e431271](https://github.com/pixelfed/pixelfed/commit/0e431271))
- Update hashtag following ([015b1b80](https://github.com/pixelfed/pixelfed/commit/015b1b80))
- Update IncrementPostCount job, prevent overlap ([b2c9cc23](https://github.com/pixelfed/pixelfed/commit/b2c9cc23))
- Update HashtagFollowService, fix cache invalidation bug ([84f4e885](https://github.com/pixelfed/pixelfed/commit/84f4e885))
- Update Experimental Home Feed, fix remote posts, shares and reblogs ([c6a6b3ae](https://github.com/pixelfed/pixelfed/commit/c6a6b3ae))
- Update HashtagService, improve count perf ([3327a008](https://github.com/pixelfed/pixelfed/commit/3327a008))
- Update StatusHashtagService, remove problematic cache layer ([e5401f85](https://github.com/pixelfed/pixelfed/commit/e5401f85))
- Update HomeFeedPipeline, fix tag filtering ([f105f4e8](https://github.com/pixelfed/pixelfed/commit/f105f4e8))
- Update HashtagService, reduce cached_count cache ttl ([15f29f7d](https://github.com/pixelfed/pixelfed/commit/15f29f7d))
- Update ApiV1Controller, fix include_reblogs param on timelines/home endpoint, and improve limit pagination logic ([287f903b](https://github.com/pixelfed/pixelfed/commit/287f903b))
- Update StoryApiV1Controller, add self-carousel endpoint. Fixes ([#4352](https://github.com/pixelfed/pixelfed/issues/4352)) ([bcb88d5b](https://github.com/pixelfed/pixelfed/commit/bcb88d5b))
- Update FollowServiceWarmCache, use more efficient query ([fe9b4c5a](https://github.com/pixelfed/pixelfed/commit/fe9b4c5a))
- Update HomeFeedPipeline, observe mutes/blocks during fanout ([8548294c](https://github.com/pixelfed/pixelfed/commit/8548294c))
- Update FederationController, add proper following/follower counts ([3204fb96](https://github.com/pixelfed/pixelfed/commit/3204fb96))
- Update FederationController, add proper statuses counts ([3204fb96](https://github.com/pixelfed/pixelfed/commit/3204fb96))
- Update Inbox handler, fix missing object_url and uri fields for direct statuses ([a0157fce](https://github.com/pixelfed/pixelfed/commit/a0157fce))
- Update DirectMessageController, deliver direct delete activities to user inbox instead of sharedInbox ([d848792a](https://github.com/pixelfed/pixelfed/commit/d848792a))
- Update DirectMessageController, dispatch deliver and delete actions to the job queue ([7f462a80](https://github.com/pixelfed/pixelfed/commit/7f462a80))
- Update Inbox, improve story attribute collection ([06bee36c](https://github.com/pixelfed/pixelfed/commit/06bee36c))
- Update DirectMessageController, dispatch local deletes to pipeline ([98186564](https://github.com/pixelfed/pixelfed/commit/98186564))
- Update StatusPipeline, fix Direct and Story notification deletion ([4c95306f](https://github.com/pixelfed/pixelfed/commit/4c95306f))
- Update Notifications.vue, fix deprecated DM action links for story activities ([4c3823b0](https://github.com/pixelfed/pixelfed/commit/4c3823b0))
- Update ComposeModal, fix missing alttext post state ([0a068119](https://github.com/pixelfed/pixelfed/commit/0a068119))
- Update PhotoAlbumPresenter.vue, fix fullscreen mode ([822e9888](https://github.com/pixelfed/pixelfed/commit/822e9888))
- Update Timeline.vue, improve CHT pagination ([9c43e7e2](https://github.com/pixelfed/pixelfed/commit/9c43e7e2))
- Update HomeFeedPipeline, fix StatusService validation ([041c0135](https://github.com/pixelfed/pixelfed/commit/041c0135))
- Update Inbox, improve tombstone query efficiency ([759a4393](https://github.com/pixelfed/pixelfed/commit/759a4393))
- Update AccountService, add setLastActive method ([ebbd98e7](https://github.com/pixelfed/pixelfed/commit/ebbd98e7))
- Update ApiV1Controller, set last_active_at ([b6419545](https://github.com/pixelfed/pixelfed/commit/b6419545))
- Update AdminShadowFilter, fix deleted profile bug ([a492a95a](https://github.com/pixelfed/pixelfed/commit/a492a95a))
- Update FollowerService, add $silent param to remove method to more efficently purge relationships ([1664a5bc](https://github.com/pixelfed/pixelfed/commit/1664a5bc))
- Update AP ProfileTransformer, add published attribute ([adfaa2b1](https://github.com/pixelfed/pixelfed/commit/adfaa2b1))
- Update meta tags, improve descriptions and seo/og tags ([fd44c80c](https://github.com/pixelfed/pixelfed/commit/fd44c80c))
- Update login view, add email prefill logic ([d76f0168](https://github.com/pixelfed/pixelfed/commit/d76f0168))
- Update LoginController, fix captcha validation error message ([0325e171](https://github.com/pixelfed/pixelfed/commit/0325e171))
- Update ApiV1Controller, properly cast boolean sensitive parameter. Fixes #4888 ([0aff126a](https://github.com/pixelfed/pixelfed/commit/0aff126a))
- Update AccountImport.vue, fix new IG export format ([59aa6a4b](https://github.com/pixelfed/pixelfed/commit/59aa6a4b))
- Update TransformImports command, fix import service condition ([32c59f04](https://github.com/pixelfed/pixelfed/commit/32c59f04))
- Update AP helpers, more efficently update post count ([7caed381](https://github.com/pixelfed/pixelfed/commit/7caed381))
- Update AP helpers, refactor post count decrement logic ([b81ae577](https://github.com/pixelfed/pixelfed/commit/b81ae577))
- Update AP helpers, fix sensitive bug ([00ed330c](https://github.com/pixelfed/pixelfed/commit/00ed330c))
- Update NotificationEpochUpdatePipeline, use more efficient query ([4d401389](https://github.com/pixelfed/pixelfed/commit/4d401389))
- Update notification pipelines, fix non-local saving ([fa97a1f3](https://github.com/pixelfed/pixelfed/commit/fa97a1f3))
- Update NodeinfoService, disable redirects ([240e6bbe](https://github.com/pixelfed/pixelfed/commit/240e6bbe))
- Update Instance model, add entity casts ([289cad47](https://github.com/pixelfed/pixelfed/commit/289cad47))
- Update FetchNodeinfoPipeline, use more efficient dispatch ([ac01f51a](https://github.com/pixelfed/pixelfed/commit/ac01f51a))
- Update horizon.php config ([1e3acade](https://github.com/pixelfed/pixelfed/commit/1e3acade))
- Update PublicApiController, consume InstanceService blocked domains for account and statuses endpoints ([01b33fb3](https://github.com/pixelfed/pixelfed/commit/01b33fb3))
- Update ApiV1Controller, enforce blocked instance domain logic ([5b284cac](https://github.com/pixelfed/pixelfed/commit/5b284cac))
- Update ApiV2Controller, add vapid key to instance object. Thanks thisismissem! ([4d02d6f1](https://github.com/pixelfed/pixelfed/commit/4d02d6f1))
## [v0.11.9 (2023-08-21)](https://github.com/pixelfed/pixelfed/compare/v0.11.8...v0.11.9)
### Added
- Import from Instagram ([#4466](https://github.com/pixelfed/pixelfed/pull/4466)) ([cf3078c5](https://github.com/pixelfed/pixelfed/commit/cf3078c5))
- Sign-in with Mastodon ([#4545](https://github.com/pixelfed/pixelfed/pull/4545)) ([45b9404e](https://github.com/pixelfed/pixelfed/commit/45b9404e))
- Health check endpoint at /api/service/health-check ([ff58f970](https://github.com/pixelfed/pixelfed/commit/ff58f970))
- Reblogs in home feed ([#4563](https://github.com/pixelfed/pixelfed/pull/4563)) ([b86d47bf](https://github.com/pixelfed/pixelfed/commit/b86d47bf))
- Account Migrations ([#4578](https://github.com/pixelfed/pixelfed/pull/4578)) ([a9220e4e](https://github.com/pixelfed/pixelfed/commit/a9220e4e))
### Updates
- Update Notifications.vue component, fix filtering logic to prevent endless spinner ([3df9b53f](https://github.com/pixelfed/pixelfed/commit/3df9b53f))
- Update Direct Messages, fix api endpoint ([fe8728c0](https://github.com/pixelfed/pixelfed/commit/fe8728c0))
- Update nginx config ([fbdc6358](https://github.com/pixelfed/pixelfed/commit/fbdc6358))
- Update api routes, add DeprecatedEndpoint middleware. For more info, visit [pixelfed.org/kb/10404](https://pixelfed.org/kb/10404) ([a8453e77](https://github.com/pixelfed/pixelfed/commit/a8453e77))
- Update admin dashboard, improve users section ([36b6bf48](https://github.com/pixelfed/pixelfed/commit/36b6bf48))
- Update AdminApiController, add instance stats endpoint ([89c3710d](https://github.com/pixelfed/pixelfed/commit/89c3710d))
- Update config, re-add `PF_MAX_USERS` .env variable to limit max users to 1000 by default ([a6d10f03](https://github.com/pixelfed/pixelfed/commit/a6d10f03))
- Update AdminApiController, fix stats ([5c5541fc](https://github.com/pixelfed/pixelfed/commit/5c5541fc))
- Update AdminApiController, include more data for getUser method ([4f850e54](https://github.com/pixelfed/pixelfed/commit/4f850e54))
- Update AdminApiController, improve admin moderation tools ([763ce19a](https://github.com/pixelfed/pixelfed/commit/763ce19a))
- Update ActivityPubFetchService, fix authorized_fetch compatibility. Closes #1850, #2713, #2935 ([63a7879c](https://github.com/pixelfed/pixelfed/commit/63a7879c))
- Update IG Import commands, fix stalled import queue ([b18f3fba](https://github.com/pixelfed/pixelfed/commit/b18f3fba))
- Update TransformImports command, improve handling of imported posts that already exist or are from deleted accounts ([892907d5](https://github.com/pixelfed/pixelfed/commit/892907d5))
- Update console kernel, add import upload gc ([afe6948d](https://github.com/pixelfed/pixelfed/commit/afe6948d))
- Update ImportService, filter deleted posts from getImportedPosts endpoint ([10dd348c](https://github.com/pixelfed/pixelfed/commit/10dd348c))
- Update FixStatusCount, improve command and support remote count resync ([04f4f8ba](https://github.com/pixelfed/pixelfed/commit/04f4f8ba))
- Update StatusRemoteUpdatePipeline, fix missing mime and size attributes that cause empty media previews on our mobile app ([ea54413e](https://github.com/pixelfed/pixelfed/commit/ea54413e))
- Update ComposeModal.vue, fix scroll issue and dont hide scrollbar ([2d959fb3](https://github.com/pixelfed/pixelfed/commit/2d959fb3))
- Update AccountImport, add select first 100 posts button ([625a76a5](https://github.com/pixelfed/pixelfed/commit/625a76a5))
- Update ApiV1Controller, add include_reblogs attribute to home timeline ([37fd0342](https://github.com/pixelfed/pixelfed/commit/37fd0342))
- Update rate limits, fixes #4537 ([1cc6274a](https://github.com/pixelfed/pixelfed/commit/1cc6274a))
- Update Services, use zpopmin on predis ([4b2c66f5](https://github.com/pixelfed/pixelfed/commit/4b2c66f5))
- Update Inbox, allow storing Create->Note activities without any local followers, disabled by default ([9fa6b3f7](https://github.com/pixelfed/pixelfed/commit/9fa6b3f7))
- Update AP Helpers, preserve admin unlisted state before adding to NetworkTimelineService ([0704c7e0](https://github.com/pixelfed/pixelfed/commit/0704c7e0))
- Update SearchApiV2Service, improve resolve query logic to better handle remote posts/profiles and local posts/profiles ([c61d0b91](https://github.com/pixelfed/pixelfed/commit/c61d0b91))
- Update FollowPipeline, improve follower/following count calculation ([0b515767](https://github.com/pixelfed/pixelfed/commit/0b515767))
- Update TransformImports command, increment status_count on profile model ([ba7551d8](https://github.com/pixelfed/pixelfed/commit/ba7551d8))
- Update AP Helpers, improve url validation and add optional dns verification, disabled by default ([2bef3e41](https://github.com/pixelfed/pixelfed/commit/2bef3e41))
- Update admin users blade view, show last_active_at and other info ([e0b48b29](https://github.com/pixelfed/pixelfed/commit/e0b48b29))
- Update MediaStorageService, improve head header handling ([3590adbd](https://github.com/pixelfed/pixelfed/commit/3590adbd))
- Update admin user view, improve previews ([ff2c16fe](https://github.com/pixelfed/pixelfed/commit/ff2c16fe))
- Update FanoutDeletePipeline, fix AP object ([0d802c31](https://github.com/pixelfed/pixelfed/commit/0d802c31))
- Update Remote Auth feature, fix custom domain bug and enforce banned domains ([acabf603](https://github.com/pixelfed/pixelfed/commit/acabf603))
- Update StatusService, reduce cache ttl from 7 days to 6 hours ([59b64378](https://github.com/pixelfed/pixelfed/commit/59b64378))
- Update ProfileController, allow albums in atom feed. Closes #4561. Fixes #4526 ([1c105a6c](https://github.com/pixelfed/pixelfed/commit/1c105a6c))
- Update admin users view, fix website value. Closes #4557 ([c469d475](https://github.com/pixelfed/pixelfed/commit/c469d475))
- Update StatusStatelessTransformer, allow unlisted reblogs ([1c13b518](https://github.com/pixelfed/pixelfed/commit/1c13b518))
- Update ApiV1Controller, hydrate reblog state in home timeline ([13bdaa2e](https://github.com/pixelfed/pixelfed/commit/13bdaa2e))
- Update Timeline component, improve reblog support ([29de91e5](https://github.com/pixelfed/pixelfed/commit/29de91e5))
- Update timeline settings, add photo reblogs only option ([e2705b9a](https://github.com/pixelfed/pixelfed/commit/e2705b9a))
- Update PostContent, add text cw warning ([911504fa](https://github.com/pixelfed/pixelfed/commit/911504fa))
- Update ActivityPubFetchService, add validateUrl parameter to bypass url validation to fetch content from blocked instances ([3d1b6516](https://github.com/pixelfed/pixelfed/commit/3d1b6516))
- Update RemoteStatusDelete pipeline ([71e92261](https://github.com/pixelfed/pixelfed/commit/71e92261))
- Update RemoteStatusDelete pipeline ([fab8f25e](https://github.com/pixelfed/pixelfed/commit/fab8f25e))
- Update RemoteStatusPipeline, fix reply check ([618b6727](https://github.com/pixelfed/pixelfed/commit/618b6727))
- Update ApiV1Controller, add bookmarked to timeline entities ([ca746717](https://github.com/pixelfed/pixelfed/commit/ca746717))
## [v0.11.8 (2023-05-29)](https://github.com/pixelfed/pixelfed/compare/v0.11.7...v0.11.8)
### API Changes
- Added `following_since` attribute to `/api/v1/accounts/relationships` endpoint when `_pe=1` (pixelfed entity) parameter is present ([992d910b](https://github.com/pixelfed/pixelfed/commit/992d910b))
- Added `/api/v1.1/accounts/app/settings` endpoint and UserAppSettings model to store app specific settings ([a2305d5f](https://github.com/pixelfed/pixelfed/commit/a2305d5f))
### Added
- Post edits ([#4416](https://github.com/pixelfed/pixelfed/pull/4416)) ([98cf8f3](https://github.com/pixelfed/pixelfed/commit/98cf8f3))
### Updates
- Update StatusService, fix bug in getFull method ([4d8b4dcf](https://github.com/pixelfed/pixelfed/commit/4d8b4dcf))
- Update Config, bump version for post edit support without having to clear cache ([c0190d84](https://github.com/pixelfed/pixelfed/commit/c0190d84))
- Update EditHistoryModal, fix caption rendering ([0f803446](https://github.com/pixelfed/pixelfed/commit/0f803446))
- Update StatusRemoteUpdatePipeline, fix typo ([109d0419](https://github.com/pixelfed/pixelfed/commit/109d0419))
- Update StatusActivityPubDeliver, fix delivery addressing ([1f2183ee](https://github.com/pixelfed/pixelfed/commit/1f2183ee))
- Update UpdateStatusService, fix formatting issue. Fixes #4423 ([4479055e](https://github.com/pixelfed/pixelfed/commit/4479055e))
- Update nginx config ([ee3b6e09](https://github.com/pixelfed/pixelfed/commit/ee3b6e09))
- Update Status model, increase max mentions, hashtags and links ([1430f532](https://github.com/pixelfed/pixelfed/commit/1430f532))
## [v0.11.7 (2023-05-24)](https://github.com/pixelfed/pixelfed/compare/v0.11.6...v0.11.7)
### API Changes
- Added [/api/v1/followed_tags](https://docs.joinmastodon.org/methods/followed_tags/) api endpoint ([175a8486](https://github.com/pixelfed/pixelfed/commit/175a8486))
- Added [/api/v1/tags/:id/follow](https://docs.joinmastodon.org/methods/tags/#follow) and [/api/v1/tags/:id/unfollow](https://docs.joinmastodon.org/methods/tags/#unfollow) api endpoints ([4d997bb9](https://github.com/pixelfed/pixelfed/commit/4d997bb9))
- Added [/api/v1/tags/:id](https://docs.joinmastodon.org/methods/tags/) api endpoint ([521b3b4c](https://github.com/pixelfed/pixelfed/commit/521b3b4c))
- Added `only_media` support to /api/v1/timelines/tag/:id api endpoint ([b5fe956a](https://github.com/pixelfed/pixelfed/commit/b5fe956a))
- Added /api/v2/instance api endpoint ([167dbcdd](https://github.com/pixelfed/pixelfed/commit/167dbcdd))
- Removed api endpoint cloud ip block logic ([6a2daf1f](https://github.com/pixelfed/pixelfed/commit/6a2daf1f))
- Added idempotency-key support to /api/v1/statuses endpoint ([c54cdd3e](https://github.com/pixelfed/pixelfed/commit/c54cdd3e))
### Added
- Added store remote media on S3 config setting, disabled by default ([51768083](https://github.com/pixelfed/pixelfed/commit/51768083))
- Added Autospam Advanced Detection ([132a58de](https://github.com/pixelfed/pixelfed/commit/132a58de))
### Updates
- Update admin dashboard, fix search and dropdown menu ([dac0d083](https://github.com/pixelfed/pixelfed/commit/dac0d083))
- Update sudo mode view, fix trusted device checkbox ([8ef900bf](https://github.com/pixelfed/pixelfed/commit/8ef900bf))
- Update SearchApiV2Service, improve postgres support ([666e5732](https://github.com/pixelfed/pixelfed/commit/666e5732))
- Update StoryController, show active self stories on home timeline ([633351f6](https://github.com/pixelfed/pixelfed/commit/633351f6))
- Update ApiV1Controller, fix trending accounts format. Closes #4356 ([37bd2ee5](https://github.com/pixelfed/pixelfed/commit/37bd2ee5))
- Update instance config, enable config cache by default ([970f77b0](https://github.com/pixelfed/pixelfed/commit/970f77b0))
- Update Admin Dashboard, allow admins to designate an admin account for the landing page and instance api endpoint ([6ea2bdc7](https://github.com/pixelfed/pixelfed/commit/6ea2bdc7))
- Update config, enable oauth by default ([6a2e9e8f](https://github.com/pixelfed/pixelfed/commit/6a2e9e8f))
- Update StatusService, fix missing account condition ([f48daab3](https://github.com/pixelfed/pixelfed/commit/f48daab3))
- Update ProfileService, add softFail param ([6bc20a37](https://github.com/pixelfed/pixelfed/commit/6bc20a37))
- Update MediaTagService, fix ProfileService to soft fail on missing or deleted accounts ([df444851](https://github.com/pixelfed/pixelfed/commit/df444851))
- Update LikeService, improve likedBy logic to soft fail on missing or deleted accounts ([91ba1398](https://github.com/pixelfed/pixelfed/commit/91ba1398))
- Update StatusTransformers, fix ProfileService to soft fail on missing or deleted accounts ([43d3aa2b](https://github.com/pixelfed/pixelfed/commit/43d3aa2b))
- Update ApiV1Controller, fix hashtag timeline ([fc1a385c](https://github.com/pixelfed/pixelfed/commit/fc1a385c))
- Update settings view, add fallback avatar ([1a83c585](https://github.com/pixelfed/pixelfed/commit/1a83c585))
- Update HashtagFollow model, add MAX_LIMIT of 250 tags per account ([ed352141](https://github.com/pixelfed/pixelfed/commit/ed352141))
- Update Notification logic, remove message and rendered fields ([6cdb5bc6](https://github.com/pixelfed/pixelfed/commit/6cdb5bc6))
- Update InstanceService, fix banner blurhash memory bug ([3aad75ab](https://github.com/pixelfed/pixelfed/commit/3aad75ab))
- Update models, remove deprecated toText and toHtml method ([ea943333](https://github.com/pixelfed/pixelfed/commit/ea943333))
- Update Notification components, add autospam notification support ([0d3b4bc2](https://github.com/pixelfed/pixelfed/commit/0d3b4bc2))
- Update AutoSpam Bouncer, generate notification on positive detections ([d5f63f8a](https://github.com/pixelfed/pixelfed/commit/d5f63f8a))
- Update admin autospam apis, remove autospam warning notifications when appropriate ([588ca653](https://github.com/pixelfed/pixelfed/commit/588ca653))
- Update StatusEntityLexer, stop saving entities ([a91a5e48](https://github.com/pixelfed/pixelfed/commit/a91a5e48))
- Update UserCreate command, fix is_admin flag ([ad25ed67](https://github.com/pixelfed/pixelfed/commit/ad25ed67))
- Update Bouncer, adjust advanced Autospam logic ([18cddd43](https://github.com/pixelfed/pixelfed/commit/18cddd43))
- Update atom view, fix atom feed bug ([63b72c42](https://github.com/pixelfed/pixelfed/commit/63b72c42))
- Update StatusController, disable post embeds from spam accounts ([c167af43](https://github.com/pixelfed/pixelfed/commit/c167af43))
- Update ProfileController, require login to view spam accounts, and disable profile embeds and atom feeds for spam accounts ([dd2f5bb9](https://github.com/pixelfed/pixelfed/commit/dd2f5bb9))
- Update Settings, allow users to disable atom feeds ([3662d3de](https://github.com/pixelfed/pixelfed/commit/3662d3de))
- Update ApiV1Controller, filter muted/blocked accounts from tag timeline ([f42c1140](https://github.com/pixelfed/pixelfed/commit/f42c1140))
- Update admin moderation logic, only re-add top level posts ([c6ffda96](https://github.com/pixelfed/pixelfed/commit/c6ffda96))
- Update admin dashboard, add mass account deletes ([b8426cce](https://github.com/pixelfed/pixelfed/commit/b8426cce))
- Update scheduler, fix S3 media garbage collection not being executed when cloud storage is enabled via dashboard without .env/config being enabled ([adb070f1](https://github.com/pixelfed/pixelfed/commit/adb070f1))
- Update MediaController, add fallback for local files that are later stored on S3 but still are referenced in cached objects remotely ([4973cb46](https://github.com/pixelfed/pixelfed/commit/4973cb46))
- Update PublicTimelineService, improve warmCache query ([9f901d65](https://github.com/pixelfed/pixelfed/commit/9f901d65))
- Update AP Inbox, fix delete handling ([2800c888](https://github.com/pixelfed/pixelfed/commit/2800c888))
- Update login/register views and captcha config, enable login or register captchas or both ([c071c719](https://github.com/pixelfed/pixelfed/commit/c071c719))
- Update login form, allow admins to enable captcha after X failed attempts. Admins can set the number of attempts before captcha is shown, default is 2 attempts before captcha is required ([221ddce0](https://github.com/pixelfed/pixelfed/commit/221ddce0))
## [v0.11.6 (2023-05-03)](https://github.com/pixelfed/pixelfed/compare/v0.11.5...v0.11.6)
### Added
- Add php 8.2 support. Bump laravel version, v9 => v10 ([fb4ac4eb](https://github.com/pixelfed/pixelfed/commit/fb4ac4eb))
- New media:fix-nonlocal-driver command. Fixes s3 media created with invalid FILESYSTEM_DRIVER=s3 configuration ([672cccd4](https://github.com/pixelfed/pixelfed/commit/672cccd4))
- New landing page design ([09c0032b](https://github.com/pixelfed/pixelfed/commit/09c0032b))
- Add cloud ip bans to BouncerService (disabled by default) ([50ab2e20](https://github.com/pixelfed/pixelfed/commit/50ab2e20))
- Redesigned Admin Dashboard Reports/Moderation ([c6cc6327](https://github.com/pixelfed/pixelfed/commit/c6cc6327))
### Fixes
- Fixed `violates check constraint "statuses_visibility_check"` bug affecting postgres instances + various api endpoints ([79b6a17e](https://github.com/pixelfed/pixelfed/commit/79b6a17e))
- Fixed duplicate hashtags on postgres ([64059cb4](https://github.com/pixelfed/pixelfed/commit/64059cb4))
- Fixed custom emoji domain search on postgres. Closes #4333 ([3dac45f3](https://github.com/pixelfed/pixelfed/commit/3dac45f3))
### Updates
- Update ApiV1Controller, fix blocking remote accounts. Closes #4256 ([8e71e0c0](https://github.com/pixelfed/pixelfed/commit/8e71e0c0))
- Update ComposeController, fix postgres location search. Closes #4242 and #4239 ([64a4a006](https://github.com/pixelfed/pixelfed/commit/64a4a006))
- Update app.js, add title attribute to iframe embeds to comply with accessibility requirements ([4d72b9e3](https://github.com/pixelfed/pixelfed/commit/4d72b9e3))
- Update MediaPathService, fix story path ([aebbad96](https://github.com/pixelfed/pixelfed/commit/aebbad96))
- Update Story v1.1 api endpoints ([855e9626](https://github.com/pixelfed/pixelfed/commit/855e9626))
- Update ApiV1Controller, filter mute/blocks on statuses/context and statuses/replies endpoints ([73aa01e8](https://github.com/pixelfed/pixelfed/commit/73aa01e8))
- Update filesystems, store all files as public by default and add default permissions. Fixes #4273, #4275. Closes #3825 ([22da2647](https://github.com/pixelfed/pixelfed/commit/22da2647))
- Update Profile model, fix avatar url path generation. Fixes #4041, Fixes #4031, Fixes #3523 ([28bf8649](https://github.com/pixelfed/pixelfed/commit/28bf8649))
- Update filesystem config, change FILESYSTEM_DRIVER env variable to DANGEROUSLY_SET_FILESYSTEM_DRIVER and remove from default env configs. Changing the default filesystem should be avoided, use FILESYSTEM_CLOUD for s3 support, otherwise you can break things ([573c88d7](https://github.com/pixelfed/pixelfed/commit/573c88d7))
- Update MediaS3GarbageCollector, fix handle ([2eee36cf](https://github.com/pixelfed/pixelfed/commit/2eee36cf))
- Update StatusController, allow users to delete replies to posts ([738925c2](https://github.com/pixelfed/pixelfed/commit/738925c2))
- Update admin autospam/report email templates, remove image previews ([76be49ac](https://github.com/pixelfed/pixelfed/commit/76be49ac))
- Update LandingService, enable landing directory/explore feed by default and move configuration to config/instance.php file ([780f2507](https://github.com/pixelfed/pixelfed/commit/780f2507))
- Update ImageOptimizePipeline, improve support for disabling image optimizations ([e76289e4](https://github.com/pixelfed/pixelfed/commit/e76289e4))
- Update LandingController, fix config variable names ([b716926b](https://github.com/pixelfed/pixelfed/commit/b716926b))
- Update Privacy Settings, add Directory setting ([634c15e4](https://github.com/pixelfed/pixelfed/commit/634c15e4))
- Update site config ([6d59dc8e](https://github.com/pixelfed/pixelfed/commit/6d59dc8e))
- Update db:raw queries to support laravel v10 ([849e5103](https://github.com/pixelfed/pixelfed/commit/849e5103))
- Update RegisterController, store client ip during registration ([d4c967de](https://github.com/pixelfed/pixelfed/commit/d4c967de))
- Update ApiV1Controller, fix account blocks. Closes #4304 ([98739139](https://github.com/pixelfed/pixelfed/commit/98739139))
- Update RegisterController, improve max_users calculation and add kb page to redirect to if conditions are met ([1bbee6d0](https://github.com/pixelfed/pixelfed/commit/1bbee6d0))
- Update SecuritySettings, remove imagick depdency for 2FA qr code generation image ([506f95c6](https://github.com/pixelfed/pixelfed/commit/506f95c6))
- Update 2fa checkpoint view design ([86c472ac](https://github.com/pixelfed/pixelfed/commit/86c472ac))
- Update sudo mode checkpoint view design ([091e0b2c](https://github.com/pixelfed/pixelfed/commit/091e0b2c))
- Update ForgotPasswordController, add captcha support, improve security and a new redesigned view ([f6e7ff64](https://github.com/pixelfed/pixelfed/commit/f6e7ff64))
- Update ResetPasswordController, add captcha support, improve security and a new redesigned view ([0ab5b96a](https://github.com/pixelfed/pixelfed/commit/0ab5b96a))
- Update Inbox, remove handleCreateActivity logic that rejected posts from accounts without followers ([a93a3efd](https://github.com/pixelfed/pixelfed/commit/a93a3efd))
- Update ApiV1Controller and DiscoverController, fix postgres hashtag search ([055aa6b3](https://github.com/pixelfed/pixelfed/commit/055aa6b3))
- Update StatusTagsPipeline, deduplicate hashtags on postgres ([867cbc75](https://github.com/pixelfed/pixelfed/commit/867cbc75))
- Update SearchApiV2Service, fix postgres hashtag search and prepend wildcard operator to improve results ([6e20d0a6](https://github.com/pixelfed/pixelfed/commit/6e20d0a6))
## [v0.11.5 (2023-03-25)](https://github.com/pixelfed/pixelfed/compare/v0.11.4...v0.11.5)
@ -985,7 +1405,7 @@
- Added post embeds ([1fecf717](https://github.com/pixelfed/pixelfed/commit/1fecf717))
- Added profile embeds ([fb7a3cf0](https://github.com/pixelfed/pixelfed/commit/fb7a3cf0))
- Added Force MetroUI labs experiment ([#1889](https://github.com/pixelfed/pixelfed/pull/1889))
- Added Stories, to enable add ```STORIES_ENABLED=true``` to ```.env``` and run ```php artisan config:cache && php artisan cache:clear```. If opcache is enabled you may need to reload the web server.
- Added Stories, to enable add ```STORIES_ENABLED=true``` to ```.env``` and run ```php artisan config:cache && php artisan cache:clear```. If opcache is enabled you may need to reload the web server.
### Fixed
- Fixed like and share/reblog count on profiles ([86cb7d09](https://github.com/pixelfed/pixelfed/commit/86cb7d09))

18
CODEOWNERS 100644
Wyświetl plik

@ -0,0 +1,18 @@
# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
* @dansup
# Docker related files
.editorconfig @jippi @dansup
.env @jippi @dansup
.env.* @jippi @dansup
.hadolint.yaml @jippi @dansup
.shellcheckrc @jippi @dansup
/.github/ @jippi @dansup
/docker/ @jippi @dansup
/tests/ @jippi @dansup
docker-compose.migrate.yml @jippi @dansup
docker-compose.yml @jippi @dansup
goss.yaml @jippi @dansup

307
Dockerfile 100644
Wyświetl plik

@ -0,0 +1,307 @@
# syntax=docker/dockerfile:1
# See https://hub.docker.com/r/docker/dockerfile
#######################################################
# Configuration
#######################################################
# See: https://github.com/mlocati/docker-php-extension-installer
ARG DOCKER_PHP_EXTENSION_INSTALLER_VERSION="2.1.80"
# See: https://github.com/composer/composer
ARG COMPOSER_VERSION="2.6"
# See: https://nginx.org/
ARG NGINX_VERSION="1.25.3"
# See: https://github.com/ddollar/forego
ARG FOREGO_VERSION="0.17.2"
# See: https://github.com/hairyhenderson/gomplate
ARG GOMPLATE_VERSION="v3.11.6"
# See: https://github.com/jippi/dottie
ARG DOTTIE_VERSION="v0.9.5"
###
# PHP base configuration
###
# See: https://hub.docker.com/_/php/tags
ARG PHP_VERSION="8.1"
# See: https://github.com/docker-library/docs/blob/master/php/README.md#image-variants
ARG PHP_BASE_TYPE="apache"
ARG PHP_DEBIAN_RELEASE="bullseye"
ARG RUNTIME_UID=33 # often called 'www-data'
ARG RUNTIME_GID=33 # often called 'www-data'
# APT extra packages
ARG APT_PACKAGES_EXTRA=
# Extensions installed via [pecl install]
# ! NOTE: imagick is installed from [master] branch on GitHub due to 8.3 bug on ARM that haven't
# ! been released yet (after +10 months)!
# ! See: https://github.com/Imagick/imagick/pull/641
ARG PHP_PECL_EXTENSIONS="redis https://codeload.github.com/Imagick/imagick/tar.gz/28f27044e435a2b203e32675e942eb8de620ee58"
ARG PHP_PECL_EXTENSIONS_EXTRA=
# Extensions installed via [docker-php-ext-install]
ARG PHP_EXTENSIONS="intl bcmath zip pcntl exif curl gd"
ARG PHP_EXTENSIONS_EXTRA=""
ARG PHP_EXTENSIONS_DATABASE="pdo_pgsql pdo_mysql pdo_sqlite"
# GPG key for nginx apt repository
ARG NGINX_GPGKEY="573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62"
# GPP key path for nginx apt repository
ARG NGINX_GPGKEY_PATH="/usr/share/keyrings/nginx-archive-keyring.gpg"
#######################################################
# Docker "copy from" images
#######################################################
# Composer docker image from Docker Hub
#
# NOTE: Docker will *not* pull this image unless it's referenced (via build target)
FROM composer:${COMPOSER_VERSION} AS composer-image
# php-extension-installer image from Docker Hub
#
# NOTE: Docker will *not* pull this image unless it's referenced (via build target)
FROM mlocati/php-extension-installer:${DOCKER_PHP_EXTENSION_INSTALLER_VERSION} AS php-extension-installer
# nginx webserver from Docker Hub.
# Used to copy some docker-entrypoint files for [nginx-runtime]
#
# NOTE: Docker will *not* pull this image unless it's referenced (via build target)
FROM nginx:${NGINX_VERSION} AS nginx-image
# Forego is a Procfile "runner" that makes it trival to run multiple
# processes under a simple init / PID 1 process.
#
# NOTE: Docker will *not* pull this image unless it's referenced (via build target)
#
# See: https://github.com/nginx-proxy/forego
FROM nginxproxy/forego:${FOREGO_VERSION}-debian AS forego-image
# Dottie makes working with .env files easier and safer
#
# NOTE: Docker will *not* pull this image unless it's referenced (via build target)
#
# See: https://github.com/jippi/dottie
FROM ghcr.io/jippi/dottie:${DOTTIE_VERSION} AS dottie-image
# gomplate-image grabs the gomplate binary from GitHub releases
#
# It's in its own layer so it can be fetched in parallel with other build steps
FROM php:${PHP_VERSION}-${PHP_BASE_TYPE}-${PHP_DEBIAN_RELEASE} AS gomplate-image
ARG TARGETARCH
ARG TARGETOS
ARG GOMPLATE_VERSION
RUN set -ex \
&& curl \
--silent \
--show-error \
--location \
--output /usr/local/bin/gomplate \
https://github.com/hairyhenderson/gomplate/releases/download/${GOMPLATE_VERSION}/gomplate_${TARGETOS}-${TARGETARCH} \
&& chmod +x /usr/local/bin/gomplate \
&& /usr/local/bin/gomplate --version
#######################################################
# Base image
#######################################################
FROM php:${PHP_VERSION}-${PHP_BASE_TYPE}-${PHP_DEBIAN_RELEASE} AS base
ARG BUILDKIT_SBOM_SCAN_STAGE="true"
ARG APT_PACKAGES_EXTRA
ARG PHP_DEBIAN_RELEASE
ARG PHP_VERSION
ARG RUNTIME_GID
ARG RUNTIME_UID
ARG TARGETPLATFORM
ENV DEBIAN_FRONTEND="noninteractive"
# Ensure we run all scripts through 'bash' rather than 'sh'
SHELL ["/bin/bash", "-c"]
RUN set -ex \
&& mkdir -pv /var/www/ \
&& chown -R ${RUNTIME_UID}:${RUNTIME_GID} /var/www
WORKDIR /var/www/
ENV APT_PACKAGES_EXTRA=${APT_PACKAGES_EXTRA}
# Install and configure base layer
COPY docker/shared/root/docker/install/base.sh /docker/install/base.sh
RUN --mount=type=cache,id=pixelfed-apt-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt \
--mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \
/docker/install/base.sh
#######################################################
# PHP: extensions
#######################################################
FROM base AS php-extensions
ARG PHP_DEBIAN_RELEASE
ARG PHP_EXTENSIONS
ARG PHP_EXTENSIONS_DATABASE
ARG PHP_EXTENSIONS_EXTRA
ARG PHP_PECL_EXTENSIONS
ARG PHP_PECL_EXTENSIONS_EXTRA
ARG PHP_VERSION
ARG TARGETPLATFORM
COPY --from=php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
COPY docker/shared/root/docker/install/php-extensions.sh /docker/install/php-extensions.sh
RUN --mount=type=cache,id=pixelfed-pear-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/tmp/pear \
--mount=type=cache,id=pixelfed-apt-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt \
--mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \
PHP_EXTENSIONS=${PHP_EXTENSIONS} \
PHP_EXTENSIONS_DATABASE=${PHP_EXTENSIONS_DATABASE} \
PHP_EXTENSIONS_EXTRA=${PHP_EXTENSIONS_EXTRA} \
PHP_PECL_EXTENSIONS=${PHP_PECL_EXTENSIONS} \
PHP_PECL_EXTENSIONS_EXTRA=${PHP_PECL_EXTENSIONS_EXTRA} \
/docker/install/php-extensions.sh
#######################################################
# PHP: composer and source code
#######################################################
FROM php-extensions AS composer-and-src
ARG PHP_VERSION
ARG PHP_DEBIAN_RELEASE
ARG RUNTIME_UID
ARG RUNTIME_GID
ARG TARGETPLATFORM
# Make sure composer cache is targeting our cache mount later
ENV COMPOSER_CACHE_DIR="/cache/composer"
# Don't enforce any memory limits for composer
ENV COMPOSER_MEMORY_LIMIT=-1
# Disable interactvitity from composer
ENV COMPOSER_NO_INTERACTION=1
# Copy composer from https://hub.docker.com/_/composer
COPY --link --from=composer-image /usr/bin/composer /usr/bin/composer
#! Changing user to runtime user
USER ${RUNTIME_UID}:${RUNTIME_GID}
# Install composer dependencies
# NOTE: we skip the autoloader generation here since we don't have all files avaliable (yet)
RUN --mount=type=cache,id=pixelfed-composer-${PHP_VERSION},sharing=locked,target=/cache/composer \
--mount=type=bind,source=composer.json,target=/var/www/composer.json \
--mount=type=bind,source=composer.lock,target=/var/www/composer.lock \
set -ex \
&& composer install --prefer-dist --no-autoloader --ignore-platform-reqs
# Copy all other files over
COPY --chown=${RUNTIME_UID}:${RUNTIME_GID} . /var/www/
#######################################################
# Runtime: base
#######################################################
FROM php-extensions AS shared-runtime
ARG RUNTIME_GID
ARG RUNTIME_UID
ENV RUNTIME_UID=${RUNTIME_UID}
ENV RUNTIME_GID=${RUNTIME_GID}
COPY --link --from=forego-image /usr/local/bin/forego /usr/local/bin/forego
COPY --link --from=dottie-image /dottie /usr/local/bin/dottie
COPY --link --from=gomplate-image /usr/local/bin/gomplate /usr/local/bin/gomplate
COPY --link --from=composer-image /usr/bin/composer /usr/bin/composer
COPY --link --from=composer-and-src --chown=${RUNTIME_UID}:${RUNTIME_GID} /var/www /var/www
#! Changing user to runtime user
USER ${RUNTIME_UID}:${RUNTIME_GID}
# Generate optimized autoloader now that we have all files around
RUN set -ex \
&& ENABLE_CONFIG_CACHE=false composer dump-autoload --optimize
USER root
# for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862
RUN set -ex \
&& cp --recursive --link --preserve=all storage storage.skel \
&& rm -rf html && ln -s public html
COPY docker/shared/root /
ENTRYPOINT ["/docker/entrypoint.sh"]
#######################################################
# Runtime: apache
#######################################################
FROM shared-runtime AS apache-runtime
COPY docker/apache/root /
RUN set -ex \
&& a2enmod rewrite remoteip proxy proxy_http \
&& a2enconf remoteip
CMD ["apache2-foreground"]
#######################################################
# Runtime: fpm
#######################################################
FROM shared-runtime AS fpm-runtime
COPY docker/fpm/root /
CMD ["php-fpm"]
#######################################################
# Runtime: nginx
#######################################################
FROM shared-runtime AS nginx-runtime
ARG NGINX_GPGKEY
ARG NGINX_GPGKEY_PATH
ARG NGINX_VERSION
ARG PHP_DEBIAN_RELEASE
ARG PHP_VERSION
ARG TARGETPLATFORM
# Install nginx dependencies
RUN --mount=type=cache,id=pixelfed-apt-lists-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt/lists \
--mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \
set -ex \
&& gpg1 --keyserver "hkp://keyserver.ubuntu.com:80" --keyserver-options timeout=10 --recv-keys "${NGINX_GPGKEY}" \
&& gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" \
&& echo "deb [signed-by=${NGINX_GPGKEY_PATH}] https://nginx.org/packages/mainline/debian/ ${PHP_DEBIAN_RELEASE} nginx" >> /etc/apt/sources.list.d/nginx.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends nginx=${NGINX_VERSION}*
# copy docker entrypoints from the *real* nginx image directly
COPY --link --from=nginx-image /docker-entrypoint.d /docker/entrypoint.d/
COPY docker/nginx/root /
COPY docker/nginx/Procfile .
STOPSIGNAL SIGQUIT
CMD ["forego", "start", "-r"]

Wyświetl plik

@ -11,7 +11,10 @@ class AccountInterstitial extends Model
*
* @var array
*/
protected $dates = ['read_at', 'appeal_requested_at'];
protected $casts = [
'read_at' => 'datetime',
'appeal_requested_at' => 'datetime'
];
public const JSON_MESSAGE = 'Please use web browser to proceed.';

Wyświetl plik

@ -6,7 +6,10 @@ use Illuminate\Database\Eloquent\Model;
class Activity extends Model
{
protected $dates = ['processed_at'];
protected $casts = [
'processed_at' => 'datetime'
];
protected $fillable = ['data', 'to_id', 'from_id', 'object_type'];
public function toProfile()

Wyświetl plik

@ -18,8 +18,7 @@ class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerToke
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return [
'created_at' => time(),
'scope' => 'read write follow push'
'created_at' => time(),
];
}
}

Wyświetl plik

@ -14,10 +14,10 @@ class Avatar extends Model
*
* @var array
*/
protected $dates = [
'deleted_at',
'last_fetched_at',
'last_processed_at'
protected $casts = [
'deleted_at' => 'datetime',
'last_fetched_at' => 'datetime',
'last_processed_at' => 'datetime'
];
protected $guarded = [];

Wyświetl plik

@ -0,0 +1,57 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\AccountService;
use App\Services\Account\AccountStatService;
use App\Status;
use App\Profile;
class AccountPostCountStatUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:account-post-count-stat-update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update post counts from recent activities';
/**
* Execute the console command.
*/
public function handle()
{
$ids = AccountStatService::getAllPostCountIncr();
if(!$ids || !count($ids)) {
return;
}
foreach($ids as $id) {
$acct = AccountService::get($id, true);
if(!$acct) {
AccountStatService::removeFromPostCount($id);
continue;
}
$statusCount = Status::whereProfileId($id)->count();
if($statusCount != $acct['statuses_count']) {
$profile = Profile::find($id);
if(!$profile) {
AccountStatService::removeFromPostCount($id);
continue;
}
$profile->status_count = $statusCount;
$profile->save();
AccountService::del($id);
}
AccountStatService::removeFromPostCount($id);
}
return;
}
}

Wyświetl plik

@ -0,0 +1,106 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\User;
use App\Models\DefaultDomainBlock;
use App\Models\UserDomainBlock;
use function Laravel\Prompts\text;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\progress;
class AddUserDomainBlock extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:add-user-domain-block';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Apply a domain block to all users';
/**
* Execute the console command.
*/
public function handle()
{
$domain = text('Enter domain you want to block');
$domain = strtolower($domain);
$domain = $this->validateDomain($domain);
if(!$domain || empty($domain)) {
$this->error('Invalid domain');
return;
}
$this->processBlocks($domain);
return;
}
protected function validateDomain($domain)
{
if(!strpos($domain, '.')) {
return;
}
if(str_starts_with($domain, 'https://')) {
$domain = str_replace('https://', '', $domain);
}
if(str_starts_with($domain, 'http://')) {
$domain = str_replace('http://', '', $domain);
}
$domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST));
$valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE);
if(!$valid) {
return;
}
if($domain === config('pixelfed.domain.app')) {
$this->error('Invalid domain');
return;
}
$confirmed = confirm('Are you sure you want to block ' . $domain . '?');
if(!$confirmed) {
return;
}
return $domain;
}
protected function processBlocks($domain)
{
DefaultDomainBlock::updateOrCreate([
'domain' => $domain
]);
progress(
label: 'Updating user domain blocks...',
steps: User::lazyById(500),
callback: fn ($user) => $this->performTask($user, $domain),
);
}
protected function performTask($user, $domain)
{
if(!$user->profile_id || $user->delete_after) {
return;
}
if($user->status != null && $user->status != 'disabled') {
return;
}
UserDomainBlock::updateOrCreate([
'profile_id' => $user->profile_id,
'domain' => $domain
]);
}
}

Wyświetl plik

@ -82,7 +82,7 @@ class AvatarStorage extends Command
$this->line(' ');
if(config_cache('pixelfed.cloud_storage')) {
if((bool) config_cache('pixelfed.cloud_storage')) {
$this->info('✅ - Cloud storage configured');
$this->line(' ');
}
@ -92,7 +92,7 @@ class AvatarStorage extends Command
$this->line(' ');
}
if(config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud')) {
if((bool) config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud')) {
$disk = Storage::disk(config_cache('filesystems.cloud'));
$exists = $disk->exists('cache/avatars/default.jpg');
$state = $exists ? '✅' : '❌';
@ -100,7 +100,7 @@ class AvatarStorage extends Command
$this->info($msg);
}
$options = config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud') ?
$options = (bool) config_cache('pixelfed.cloud_storage') && config('instance.avatar.local_to_cloud') ?
[
'Cancel',
'Upload default avatar to cloud',
@ -164,7 +164,7 @@ class AvatarStorage extends Command
protected function uploadAvatarsToCloud()
{
if(!config_cache('pixelfed.cloud_storage') || !config('instance.avatar.local_to_cloud')) {
if(!(bool) config_cache('pixelfed.cloud_storage') || !config('instance.avatar.local_to_cloud')) {
$this->error('Enable cloud storage and avatar cloud storage to perform this action');
return;
}
@ -213,7 +213,7 @@ class AvatarStorage extends Command
return;
}
if(config_cache('pixelfed.cloud_storage') == false && config_cache('federation.avatars.store_local') == false) {
if((bool) config_cache('pixelfed.cloud_storage') == false && config_cache('federation.avatars.store_local') == false) {
$this->error('You have cloud storage disabled and local avatar storage disabled, we cannot refetch avatars.');
return;
}

Wyświetl plik

@ -0,0 +1,115 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Cache;
use Storage;
use App\Avatar;
use App\Jobs\AvatarPipeline\AvatarStorageCleanup;
class AvatarStorageDeepClean extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'avatar:storage-deep-clean';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Cleanup avatar storage';
protected $shouldKeepRunning = true;
protected $counter = 0;
/**
* Execute the console command.
*/
public function handle(): void
{
$this->info(' ____ _ ______ __ ');
$this->info(' / __ \(_) _____ / / __/__ ____/ / ');
$this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
$this->info(' / ____/ /> </ __/ / __/ __/ /_/ / ');
$this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
$this->info(' ');
$this->info(' Pixelfed Avatar Deep Cleaner');
$this->line(' ');
$this->info(' Purge/delete old and outdated avatars from remote accounts');
$this->line(' ');
$storage = [
'cloud' => (bool) config_cache('pixelfed.cloud_storage'),
'local' => boolval(config_cache('federation.avatars.store_local'))
];
if(!$storage['cloud'] && !$storage['local']) {
$this->error('Remote avatars are not cached locally, there is nothing to purge. Aborting...');
exit;
}
$start = 0;
if(!$this->confirm('Are you sure you want to proceed?')) {
$this->error('Aborting...');
exit;
}
if(!$this->activeCheck()) {
$this->info('Found existing deep cleaning job');
if(!$this->confirm('Do you want to continue where you left off?')) {
$this->error('Aborting...');
exit;
} else {
$start = Cache::has('cmd:asdp') ? (int) Cache::get('cmd:asdp') : (int) Storage::get('avatar-deep-clean.json');
if($start && $start < 1 || $start > PHP_INT_MAX) {
$this->error('Error fetching cached value');
$this->error('Aborting...');
exit;
}
}
}
$count = Avatar::whereNotNull('cdn_url')->where('is_remote', true)->where('id', '>', $start)->count();
$bar = $this->output->createProgressBar($count);
foreach(Avatar::whereNotNull('cdn_url')->where('is_remote', true)->where('id', '>', $start)->lazyById(10, 'id') as $avatar) {
usleep(random_int(50, 1000));
$this->counter++;
$this->handleAvatar($avatar);
$bar->advance();
}
$bar->finish();
}
protected function updateCache($id)
{
Cache::put('cmd:asdp', $id);
if($this->counter % 5 === 0) {
Storage::put('avatar-deep-clean.json', $id);
}
}
protected function activeCheck()
{
if(Storage::exists('avatar-deep-clean.json') || Cache::has('cmd:asdp')) {
return false;
}
return true;
}
protected function handleAvatar($avatar)
{
$this->updateCache($avatar->id);
$queues = ['feed', 'mmo', 'feed', 'mmo', 'feed', 'feed', 'mmo', 'low'];
$queue = $queues[random_int(0, 7)];
AvatarStorageCleanup::dispatch($avatar)->onQueue($queue);
}
}

Wyświetl plik

@ -35,12 +35,16 @@ class CloudMediaMigrate extends Command
*/
public function handle()
{
$enabled = config('pixelfed.cloud_storage');
$enabled = (bool) config_cache('pixelfed.cloud_storage');
if(!$enabled) {
$this->error('Cloud storage not enabled. Exiting...');
return;
}
if(!$this->confirm('Are you sure you want to proceed?')) {
return;
}
$limit = $this->option('limit');
$hugeMode = $this->option('huge');

Wyświetl plik

@ -0,0 +1,96 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\User;
use App\Models\DefaultDomainBlock;
use App\Models\UserDomainBlock;
use function Laravel\Prompts\text;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\progress;
class DeleteUserDomainBlock extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:delete-user-domain-block';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove a domain block for all users';
/**
* Execute the console command.
*/
public function handle()
{
$domain = text('Enter domain you want to unblock');
$domain = strtolower($domain);
$domain = $this->validateDomain($domain);
if(!$domain || empty($domain)) {
$this->error('Invalid domain');
return;
}
$this->processUnblocks($domain);
return;
}
protected function validateDomain($domain)
{
if(!strpos($domain, '.')) {
return;
}
if(str_starts_with($domain, 'https://')) {
$domain = str_replace('https://', '', $domain);
}
if(str_starts_with($domain, 'http://')) {
$domain = str_replace('http://', '', $domain);
}
$domain = strtolower(parse_url('https://' . $domain, PHP_URL_HOST));
$valid = filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME|FILTER_NULL_ON_FAILURE);
if(!$valid) {
return;
}
if($domain === config('pixelfed.domain.app')) {
return;
}
$confirmed = confirm('Are you sure you want to unblock ' . $domain . '?');
if(!$confirmed) {
return;
}
return $domain;
}
protected function processUnblocks($domain)
{
DefaultDomainBlock::whereDomain($domain)->delete();
if(!UserDomainBlock::whereDomain($domain)->count()) {
$this->info('No results found!');
return;
}
progress(
label: 'Updating user domain blocks...',
steps: UserDomainBlock::whereDomain($domain)->lazyById(500),
callback: fn ($domainBlock) => $this->performTask($domainBlock),
);
}
protected function performTask($domainBlock)
{
$domainBlock->deleteQuietly();
}
}

Wyświetl plik

@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use App\Media;
use App\Services\MediaService;
use App\Services\StatusService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class FetchMissingMediaMimeType extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:fetch-missing-media-mime-type';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
foreach (Media::whereNotNull(['remote_url', 'status_id'])->whereNull('mime')->lazyByIdDesc(50, 'id') as $media) {
$res = Http::retry(2, 100, throw: false)->head($media->remote_url);
if (! $res->successful()) {
continue;
}
if (! in_array($res->header('content-type'), explode(',', config_cache('pixelfed.media_types')))) {
continue;
}
$media->mime = $res->header('content-type');
if ($res->hasHeader('content-length')) {
$media->size = $res->header('content-length');
}
$media->save();
MediaService::del($media->status_id);
StatusService::del($media->status_id);
$this->info('mid:'.$media->id.' ('.$res->header('content-type').':'.$res->header('content-length').' bytes)');
}
}
}

Wyświetl plik

@ -0,0 +1,133 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use App\Media;
use League\Flysystem\MountManager;
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
use App\Jobs\MediaPipeline\MediaFixLocalFilesystemCleanupPipeline;
class FixMediaDriver extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'media:fix-nonlocal-driver';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Fix filesystem when FILESYSTEM_DRIVER not set to local';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
if(config('filesystems.default') !== 'local') {
$this->error('Invalid default filesystem, set FILESYSTEM_DRIVER=local to proceed');
return Command::SUCCESS;
}
if((bool) config_cache('pixelfed.cloud_storage') == false) {
$this->error('Cloud storage not enabled, exiting...');
return Command::SUCCESS;
}
$this->info(' ____ _ ______ __ ');
$this->info(' / __ \(_) _____ / / __/__ ____/ / ');
$this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
$this->info(' / ____/ /> </ __/ / __/ __/ /_/ / ');
$this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
$this->info(' ');
$this->info(' Media Filesystem Fix');
$this->info(' =====================');
$this->info(' Fix media that was created when FILESYSTEM_DRIVER=local');
$this->info(' was not properly set. This command will fix media urls');
$this->info(' and optionally optimize/generate thumbnails when applicable,');
$this->info(' clean up temporary local media files and clear the app cache');
$this->info(' to fix media paths/urls.');
$this->info(' ');
$this->error(' Remember, FILESYSTEM_DRIVER=local must remain set or you will break things!');
if(!$this->confirm('Are you sure you want to perform this command?')) {
$this->info('Exiting...');
return Command::SUCCESS;
}
$optimize = $this->choice(
'Do you want to optimize media and generate thumbnails? This will store s3 locally and re-upload optimized versions.',
['no', 'yes'],
1
);
$cloud = Storage::disk(config('filesystems.cloud'));
$mountManager = new MountManager([
's3' => $cloud->getDriver(),
'local' => Storage::disk('local')->getDriver(),
]);
$this->info('Fixing media, this may take a while...');
$this->line(' ');
$bar = $this->output->createProgressBar(Media::whereNotNull('status_id')->whereNull('cdn_url')->count());
$bar->start();
foreach(Media::whereNotNull('status_id')->whereNull('cdn_url')->lazyById(20) as $media) {
if($cloud->exists($media->media_path)) {
if($optimize === 'yes') {
$mountManager->copy(
's3://' . $media->media_path,
'local://' . $media->media_path
);
sleep(1);
if(empty($media->original_sha256)) {
$hash = \hash_file('sha256', Storage::disk('local')->path($media->media_path));
$media->original_sha256 = $hash;
$media->save();
sleep(1);
}
if(
$media->mime &&
in_array($media->mime, [
'image/jpeg',
'image/png',
'image/webp'
])
) {
ImageOptimize::dispatch($media);
sleep(3);
}
} else {
$media->cdn_url = $cloud->url($media->media_path);
$media->save();
}
}
$bar->advance();
}
$bar->finish();
$this->line(' ');
$this->line(' ');
$this->callSilently('cache:clear');
$this->info('Successfully fixed media paths and cleared cached!');
if($optimize === 'yes') {
MediaFixLocalFilesystemCleanupPipeline::dispatch()->delay(now()->addMinutes(15))->onQueue('default');
$this->line(' ');
$this->info('A cleanup job has been dispatched to delete media stored locally, it may take a few minutes to process!');
}
$this->line(' ');
return Command::SUCCESS;
}
}

Wyświetl plik

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Profile;
use App\Services\AccountService;
class FixStatusCount extends Command
{
@ -12,7 +13,7 @@ class FixStatusCount extends Command
*
* @var string
*/
protected $signature = 'fix:statuscount';
protected $signature = 'fix:statuscount {--remote} {--resync} {--remote-only} {--dlog}';
/**
* The console command description.
@ -38,18 +39,100 @@ class FixStatusCount extends Command
*/
public function handle()
{
Profile::whereNull('domain')
->chunk(50, function($profiles) {
foreach($profiles as $profile) {
$profile->status_count = $profile->statuses()
->getQuery()
->whereIn('type', ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'])
->whereNull('in_reply_to_id')
->whereNull('reblog_of_id')
->count();
$profile->save();
if(!$this->confirm('Are you sure you want to run the fix status command?')) {
return;
}
$this->line(' ');
$this->info('Running fix status command...');
$now = now();
$nulls = ['domain', 'status', 'last_fetched_at'];
$resync = $this->option('resync');
$resync24hours = false;
if($resync) {
$resyncChoices = ['Only resync accounts that havent been synced in 24 hours', 'Resync all accounts'];
$rsc = $this->choice(
'Do you want to resync all accounts, or just accounts that havent been resynced for 24 hours?',
$resyncChoices,
0
);
$rsci = array_search($rsc, $resyncChoices);
if($rsci === 0) {
$resync24hours = true;
$nulls = ['status', 'domain', 'last_fetched_at'];
} else {
$resync24hours = false;
$nulls = ['status', 'domain'];
}
});
}
$remote = $this->option('remote');
if($remote) {
$ni = array_search('domain', $nulls);
unset($nulls[$ni]);
$ni = array_search('last_fetched_at', $nulls);
unset($nulls[$ni]);
}
$remoteOnly = $this->option('remote-only');
if($remoteOnly) {
$ni = array_search('domain', $nulls);
unset($nulls[$ni]);
$ni = array_search('last_fetched_at', $nulls);
unset($nulls[$ni]);
$nulls[] = 'user_id';
}
$dlog = $this->option('dlog');
$nulls = array_values($nulls);
foreach(
Profile::when($resync24hours, function($query, $resync24hours) use($nulls) {
if(in_array('domain', $nulls)) {
return $query->whereNull('domain')
->whereNull('last_fetched_at')
->orWhere('last_fetched_at', '<', now()->subHours(24));
} else {
return $query->whereNull('last_fetched_at')
->orWhere('last_fetched_at', '<', now()->subHours(24));
}
})
->when($remoteOnly, function($query, $remoteOnly) {
return $query->whereNull('last_fetched_at')
->orWhere('last_fetched_at', '<', now()->subHours(24));
})
->whereNull($nulls)
->lazyById(50, 'id') as $profile
) {
$ogc = $profile->status_count;
$upc = $profile->statuses()
->getQuery()
->whereIn('scope', ['public', 'private', 'unlisted'])
->count();
if($ogc != $upc) {
$profile->status_count = $upc;
$profile->last_fetched_at = $now;
$profile->save();
AccountService::del($profile->id);
if($dlog) {
$this->info($profile->id . ':' . $profile->username . ' : ' . $upc);
}
} else {
$profile->last_fetched_at = $now;
$profile->save();
if($dlog) {
$this->info($profile->id . ':' . $profile->username . ' : ' . $upc);
}
}
}
$this->line(' ');
$this->info('Finished fix status count command!');
return 0;
}

Wyświetl plik

@ -0,0 +1,57 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Hashtag;
use App\StatusHashtag;
use DB;
class HashtagCachedCountUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:hashtag-cached-count-update {--limit=100}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update cached counter of hashtags';
/**
* Execute the console command.
*/
public function handle()
{
$limit = $this->option('limit');
$tags = Hashtag::whereNull('cached_count')->limit($limit)->get();
$count = count($tags);
if(!$count) {
return;
}
$bar = $this->output->createProgressBar($count);
$bar->start();
foreach($tags as $tag) {
$count = DB::table('status_hashtags')->whereHashtagId($tag->id)->count();
if(!$count) {
$tag->cached_count = 0;
$tag->saveQuietly();
$bar->advance();
continue;
}
$tag->cached_count = $count;
$tag->saveQuietly();
$bar->advance();
}
$bar->finish();
$this->line(' ');
return;
}
}

Wyświetl plik

@ -0,0 +1,94 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Hashtag;
use App\StatusHashtag;
use App\Models\HashtagRelated;
use App\Services\HashtagRelatedService;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use function Laravel\Prompts\multiselect;
use function Laravel\Prompts\confirm;
class HashtagRelatedGenerate extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:hashtag-related-generate {tag}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'tag' => 'Which hashtag should we generate related tags for?',
];
}
/**
* Execute the console command.
*/
public function handle()
{
$tag = $this->argument('tag');
$hashtag = Hashtag::whereName($tag)->orWhere('slug', $tag)->first();
if(!$hashtag) {
$this->error('Hashtag not found, aborting...');
exit;
}
$exists = HashtagRelated::whereHashtagId($hashtag->id)->exists();
if($exists) {
$confirmed = confirm('Found existing related tags, do you want to regenerate them?');
if(!$confirmed) {
$this->error('Aborting...');
exit;
}
}
$this->info('Looking up #' . $tag . '...');
$tags = StatusHashtag::whereHashtagId($hashtag->id)->count();
if(!$tags || $tags < 100) {
$this->error('Not enough posts found to generate related hashtags!');
exit;
}
$this->info('Found ' . $tags . ' posts that use that hashtag');
$related = collect(HashtagRelatedService::fetchRelatedTags($tag));
$selected = multiselect(
label: 'Which tags do you want to generate?',
options: $related->pluck('name'),
required: true,
);
$filtered = $related->filter(fn($i) => in_array($i['name'], $selected))->all();
$agg_score = $related->filter(fn($i) => in_array($i['name'], $selected))->sum('related_count');
HashtagRelated::updateOrCreate([
'hashtag_id' => $hashtag->id,
], [
'related_tags' => array_values($filtered),
'agg_score' => $agg_score,
'last_calculated_at' => now()
]);
$this->info('Finished!');
}
}

Wyświetl plik

@ -0,0 +1,118 @@
<?php
namespace App\Console\Commands;
use App\Models\CustomEmoji;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
class ImportEmojis extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'import:emojis
{path : Path to a tar.gz archive with the emojis}
{--prefix : Define a prefix for the emjoi shortcode}
{--suffix : Define a suffix for the emjoi shortcode}
{--overwrite : Overwrite existing emojis}
{--disabled : Import all emojis as disabled}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import emojis to the database';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$path = $this->argument('path');
if (!file_exists($path) || !mime_content_type($path) == 'application/x-tar') {
$this->error('Path does not exist or is not a tarfile');
return Command::FAILURE;
}
$imported = 0;
$skipped = 0;
$failed = 0;
$tar = new \PharData($path);
$tar->decompress();
foreach (new \RecursiveIteratorIterator($tar) as $entry) {
$this->line("Processing {$entry->getFilename()}");
if (!$entry->isFile() || !$this->isImage($entry) || !$this->isEmoji($entry->getPathname())) {
$failed++;
continue;
}
$filename = pathinfo($entry->getFilename(), PATHINFO_FILENAME);
$extension = pathinfo($entry->getFilename(), PATHINFO_EXTENSION);
// Skip macOS shadow files
if (str_starts_with($filename, '._')) {
continue;
}
$shortcode = implode('', [
$this->option('prefix'),
$filename,
$this->option('suffix'),
]);
$customEmoji = CustomEmoji::whereShortcode($shortcode)->first();
if ($customEmoji && !$this->option('overwrite')) {
$skipped++;
continue;
}
$emoji = $customEmoji ?? new CustomEmoji();
$emoji->shortcode = $shortcode;
$emoji->domain = config('pixelfed.domain.app');
$emoji->disabled = $this->option('disabled');
$emoji->save();
$fileName = $emoji->id . '.' . $extension;
Storage::putFileAs('public/emoji', $entry->getPathname(), $fileName);
$emoji->media_path = 'emoji/' . $fileName;
$emoji->save();
$imported++;
Cache::forget('pf:custom_emoji');
}
$this->line("Imported: {$imported}");
$this->line("Skipped: {$skipped}");
$this->line("Failed: {$failed}");
//delete file
unlink(str_replace('.tar.gz', '.tar', $path));
return Command::SUCCESS;
}
private function isImage($file)
{
$image = getimagesize($file->getPathname());
return $image !== false;
}
private function isEmoji($filename)
{
$allowedMimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
$mimeType = mime_content_type($filename);
return in_array($mimeType, $allowedMimeTypes);
}
}

Wyświetl plik

@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use App\User;
use App\Models\ImportPost;
use App\Services\ImportService;
class ImportRemoveDeletedAccounts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:import-remove-deleted-accounts';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
const CACHE_KEY = 'pf:services:import:gc-accounts:skip_min_id';
/**
* Execute the console command.
*/
public function handle()
{
$skipMinId = Cache::remember(self::CACHE_KEY, 864000, function() {
return 1;
});
$deletedIds = User::withTrashed()
->whereNotNull('status')
->whereIn('status', ['deleted', 'delete'])
->where('id', '>', $skipMinId)
->limit(500)
->pluck('id');
if(!$deletedIds || !$deletedIds->count()) {
return;
}
foreach($deletedIds as $did) {
if(Storage::exists('imports/' . $did)) {
Storage::deleteDirectory('imports/' . $did);
}
ImportPost::where('user_id', $did)->delete();
$skipMinId = $did;
}
Cache::put(self::CACHE_KEY, $skipMinId, 864000);
}
}

Wyświetl plik

@ -0,0 +1,42 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\ImportPost;
use Storage;
use App\Services\ImportService;
use App\User;
class ImportUploadCleanStorage extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:import-upload-clean-storage';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$dirs = Storage::allDirectories('imports');
foreach($dirs as $dir) {
$uid = last(explode('/', $dir));
$skip = User::whereNull('status')->find($uid);
if(!$skip) {
Storage::deleteDirectory($dir);
}
}
}
}

Wyświetl plik

@ -0,0 +1,49 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\ImportPost;
use Storage;
use App\Services\ImportService;
class ImportUploadGarbageCollection extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:import-upload-garbage-collection';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
if(!config('import.instagram.enabled')) {
return;
}
$ips = ImportPost::whereNull('status_id')->where('skip_missing_media', true)->take(100)->get();
if(!$ips->count()) {
return;
}
foreach($ips as $ip) {
$pid = $ip->profile_id;
$ip->delete();
ImportService::getPostCount($pid, true);
ImportService::clearAttempts($pid);
ImportService::getImportedFiles($pid, true);
}
}
}

Wyświetl plik

@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\ImportPost;
use App\Jobs\ImportPipeline\ImportMediaToCloudPipeline;
use function Laravel\Prompts\progress;
class ImportUploadMediaToCloudStorage extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:import-upload-media-to-cloud-storage {--limit=500}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Migrate media imported from Instagram to S3 cloud storage.';
/**
* Execute the console command.
*/
public function handle()
{
if(
(bool) config('import.instagram.storage.cloud.enabled') === false ||
(bool) config_cache('pixelfed.cloud_storage') === false
) {
$this->error('Aborted. Cloud storage is not enabled for IG imports.');
return;
}
$limit = $this->option('limit');
$progress = progress(label: 'Migrating import media', steps: $limit);
$progress->start();
$posts = ImportPost::whereUploadedToS3(false)->take($limit)->get();
foreach($posts as $post) {
ImportMediaToCloudPipeline::dispatch($post)->onQueue('low');
$progress->advance();
}
$progress->finish();
}
}

Wyświetl plik

@ -0,0 +1,298 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Instance;
use App\Profile;
use App\Services\InstanceService;
use App\Jobs\InstancePipeline\FetchNodeinfoPipeline;
use function Laravel\Prompts\select;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\progress;
use function Laravel\Prompts\search;
use function Laravel\Prompts\table;
class InstanceManager extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:instance-manager';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Manage Instances';
/**
* Execute the console command.
*/
public function handle()
{
$action = select(
'What action do you want to perform?',
[
'Recalculate Stats',
'Ban Instance',
'Unlist Instance',
'Unlisted Instances',
'Banned Instances',
'Unban Instance',
'Relist Instance',
],
);
switch($action) {
case 'Recalculate Stats':
return $this->recalculateStats();
break;
case 'Unlisted Instances':
return $this->viewUnlistedInstances();
break;
case 'Banned Instances':
return $this->viewBannedInstances();
break;
case 'Unlist Instance':
return $this->unlistInstance();
break;
case 'Ban Instance':
return $this->banInstance();
break;
case 'Unban Instance':
return $this->unbanInstance();
break;
case 'Relist Instance':
return $this->relistInstance();
break;
}
}
protected function recalculateStats()
{
$instanceCount = Instance::count();
$confirmed = confirm('Do you want to recalculate stats for all ' . $instanceCount . ' instances?');
if(!$confirmed) {
$this->error('Aborting...');
exit;
}
$users = progress(
label: 'Updating instance stats...',
steps: Instance::all(),
callback: fn ($instance) => $this->updateInstanceStats($instance),
);
}
protected function updateInstanceStats($instance)
{
FetchNodeinfoPipeline::dispatch($instance)->onQueue('intbg');
}
protected function unlistInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereUnlisted(false)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to unlist this instance?');
if(!$confirmed) {
$this->error('Aborting instance unlisting');
exit;
}
$instance->unlisted = true;
$instance->save();
InstanceService::refresh();
$this->info('Successfully unlisted ' . $instance->domain . '!');
exit;
}
protected function relistInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereUnlisted(true)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to re-list this instance?');
if(!$confirmed) {
$this->error('Aborting instance re-listing');
exit;
}
$instance->unlisted = false;
$instance->save();
InstanceService::refresh();
$this->info('Successfully re-listed ' . $instance->domain . '!');
exit;
}
protected function banInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereBanned(false)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to ban this instance?');
if(!$confirmed) {
$this->error('Aborting instance ban');
exit;
}
$instance->banned = true;
$instance->save();
InstanceService::refresh();
$this->info('Successfully banned ' . $instance->domain . '!');
exit;
}
protected function unbanInstance()
{
$id = search(
'Search by domain',
fn (string $value) => strlen($value) > 0
? Instance::whereBanned(true)->where('domain', 'like', "%{$value}%")->pluck('domain', 'id')->all()
: []
);
$instance = Instance::find($id);
if(!$instance) {
$this->error('Oops, an error occured');
exit;
}
$tbl = [
[
$instance->domain,
number_format($instance->status_count),
number_format($instance->user_count),
]
];
table(
['Domain', 'Status Count', 'User Count'],
$tbl
);
$confirmed = confirm('Are you sure you want to unban this instance?');
if(!$confirmed) {
$this->error('Aborting instance unban');
exit;
}
$instance->banned = false;
$instance->save();
InstanceService::refresh();
$this->info('Successfully un-banned ' . $instance->domain . '!');
exit;
}
protected function viewBannedInstances()
{
$data = Instance::whereBanned(true)
->get(['domain', 'user_count', 'status_count'])
->map(function($d) {
return [
'domain' => $d->domain,
'user_count' => number_format($d->user_count),
'status_count' => number_format($d->status_count),
];
})
->toArray();
table(
['Domain', 'User Count', 'Status Count'],
$data
);
}
protected function viewUnlistedInstances()
{
$data = Instance::whereUnlisted(true)
->get(['domain', 'user_count', 'status_count', 'banned'])
->map(function($d) {
return [
'domain' => $d->domain,
'user_count' => number_format($d->user_count),
'status_count' => number_format($d->status_count),
'banned' => $d->banned ? '✅' : null
];
})
->toArray();
table(
['Domain', 'User Count', 'Status Count', 'Banned'],
$data
);
}
}

Wyświetl plik

@ -0,0 +1,140 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Media;
use Cache, Storage;
use Illuminate\Contracts\Console\PromptsForMissingInput;
class MediaCloudUrlRewrite extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'media:cloud-url-rewrite {oldDomain} {newDomain}';
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'oldDomain' => 'The old S3 domain',
'newDomain' => 'The new S3 domain'
];
}
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rewrite S3 media urls from local users';
/**
* Execute the console command.
*/
public function handle()
{
$this->preflightCheck();
$this->bootMessage();
$this->confirmCloudUrl();
}
protected function preflightCheck()
{
if(!(bool) config_cache('pixelfed.cloud_storage')) {
$this->info('Error: Cloud storage is not enabled!');
$this->error('Aborting...');
exit;
}
}
protected function bootMessage()
{
$this->info(' ____ _ ______ __ ');
$this->info(' / __ \(_) _____ / / __/__ ____/ / ');
$this->info(' / /_/ / / |/_/ _ \/ / /_/ _ \/ __ / ');
$this->info(' / ____/ /> </ __/ / __/ __/ /_/ / ');
$this->info(' /_/ /_/_/|_|\___/_/_/ \___/\__,_/ ');
$this->info(' ');
$this->info(' Media Cloud Url Rewrite Tool');
$this->info(' ===');
$this->info(' Old S3: ' . trim($this->argument('oldDomain')));
$this->info(' New S3: ' . trim($this->argument('newDomain')));
$this->info(' ');
}
protected function confirmCloudUrl()
{
$disk = Storage::disk(config('filesystems.cloud'))->url('test');
$domain = parse_url($disk, PHP_URL_HOST);
if(trim($this->argument('newDomain')) !== $domain) {
$this->error('Error: The new S3 domain you entered is not currently configured');
exit;
}
if(!$this->confirm('Confirm this is correct')) {
$this->error('Aborting...');
exit;
}
$this->updateUrls();
}
protected function updateUrls()
{
$this->info('Updating urls...');
$oldDomain = trim($this->argument('oldDomain'));
$newDomain = trim($this->argument('newDomain'));
$disk = Storage::disk(config('filesystems.cloud'));
$count = Media::whereNotNull('cdn_url')->count();
$bar = $this->output->createProgressBar($count);
$counter = 0;
$bar->start();
foreach(Media::whereNotNull('cdn_url')->lazyById(1000, 'id') as $media) {
if(strncmp($media->media_path, 'http', 4) === 0) {
$bar->advance();
continue;
}
$cdnHost = parse_url($media->cdn_url, PHP_URL_HOST);
if($oldDomain != $cdnHost || $newDomain == $cdnHost) {
$bar->advance();
continue;
}
$media->cdn_url = str_replace($oldDomain, $newDomain, $media->cdn_url);
if($media->thumbnail_url != null) {
$thumbHost = parse_url($media->thumbnail_url, PHP_URL_HOST);
if($thumbHost == $oldDomain) {
$thumbUrl = $disk->url($media->thumbnail_path);
$media->thumbnail_url = $thumbUrl;
}
}
if($media->optimized_url != null) {
$optiHost = parse_url($media->optimized_url, PHP_URL_HOST);
if($optiHost == $oldDomain) {
$optiUrl = str_replace($oldDomain, $newDomain, $media->optimized_url);
$media->optimized_url = $optiUrl;
}
}
$media->save();
$counter++;
$bar->advance();
}
$bar->finish();
$this->line(' ');
$this->info('Finished! Updated ' . $counter . ' total records!');
$this->line(' ');
$this->info('Tip: Run `php artisan cache:clear` to purge cached urls');
}
}

Wyświetl plik

@ -45,7 +45,7 @@ class MediaS3GarbageCollector extends Command
*/
public function handle()
{
$enabled = config('pixelfed.cloud_storage');
$enabled = (bool) config_cache('pixelfed.cloud_storage');
if(!$enabled) {
$this->error('Cloud storage not enabled. Exiting...');
return;
@ -66,7 +66,13 @@ class MediaS3GarbageCollector extends Command
return;
}
$minId = Media::orderByDesc('id')->where('created_at', '<', now()->subHours(12))->first()->id;
$minId = Media::orderByDesc('id')->where('created_at', '<', now()->subHours(12))->first();
if(!$minId) {
return;
} else {
$minId = $minId->id;
}
return $hugeMode ?
$this->hugeMode($minId, $limit, $log) :

Wyświetl plik

@ -0,0 +1,31 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Jobs\InternalPipeline\NotificationEpochUpdatePipeline;
class NotificationEpochUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:notification-epoch-update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update notification epoch';
/**
* Execute the console command.
*/
public function handle()
{
NotificationEpochUpdatePipeline::dispatch();
}
}

Wyświetl plik

@ -0,0 +1,37 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\Internal\SoftwareUpdateService;
use Cache;
class SoftwareUpdateRefresh extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:software-update-refresh';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Refresh latest software version data';
/**
* Execute the console command.
*/
public function handle()
{
$key = SoftwareUpdateService::cacheKey();
Cache::forget($key);
Cache::remember($key, 1209600, function() {
return SoftwareUpdateService::fetchLatest();
});
$this->info('Succesfully updated software versions!');
}
}

Wyświetl plik

@ -0,0 +1,153 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\ImportPost;
use App\Services\ImportService;
use App\Media;
use App\Profile;
use App\Status;
use Storage;
use App\Services\AccountService;
use App\Services\MediaPathService;
use Illuminate\Support\Str;
use App\Util\Lexer\Autolink;
class TransformImports extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:transform-imports';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Transform imports into statuses';
/**
* Execute the console command.
*/
public function handle()
{
if(!config('import.instagram.enabled')) {
return;
}
$ips = ImportPost::whereNull('status_id')->where('skip_missing_media', '!=', true)->take(500)->get();
if(!$ips->count()) {
return;
}
foreach($ips as $ip) {
$id = $ip->user_id;
$pid = $ip->profile_id;
$profile = Profile::find($pid);
if(!$profile) {
$ip->skip_missing_media = true;
$ip->save();
continue;
}
$exists = ImportPost::whereUserId($id)
->whereNotNull('status_id')
->where('filename', $ip->filename)
->where('creation_year', $ip->creation_year)
->where('creation_month', $ip->creation_month)
->where('creation_day', $ip->creation_day)
->exists();
if($exists == true) {
$ip->skip_missing_media = true;
$ip->save();
continue;
}
$idk = ImportService::getId($ip->user_id, $ip->creation_year, $ip->creation_month, $ip->creation_day);
if(!$idk) {
$ip->skip_missing_media = true;
$ip->save();
continue;
}
if(Storage::exists('imports/' . $id . '/' . $ip->filename) === false) {
ImportService::clearAttempts($profile->id);
ImportService::getPostCount($profile->id, true);
$ip->skip_missing_media = true;
$ip->save();
continue;
}
$missingMedia = false;
foreach($ip->media as $ipm) {
$fileName = last(explode('/', $ipm['uri']));
$og = 'imports/' . $id . '/' . $fileName;
if(!Storage::exists($og)) {
$missingMedia = true;
}
}
if($missingMedia === true) {
$ip->skip_missing_media = true;
$ip->save();
continue;
}
$caption = $ip->caption;
$status = new Status;
$status->profile_id = $pid;
$status->caption = $caption;
$status->rendered = strlen(trim($caption)) ? Autolink::create()->autolink($ip->caption) : null;
$status->type = $ip->post_type;
$status->scope = 'unlisted';
$status->visibility = 'unlisted';
$status->id = $idk['id'];
$status->created_at = now()->parse($ip->creation_date);
$status->save();
foreach($ip->media as $ipm) {
$fileName = last(explode('/', $ipm['uri']));
$ext = last(explode('.', $fileName));
$basePath = MediaPathService::get($profile);
$og = 'imports/' . $id . '/' . $fileName;
if(!Storage::exists($og)) {
$ip->skip_missing_media = true;
$ip->save();
continue;
}
$size = Storage::size($og);
$mime = Storage::mimeType($og);
$newFile = Str::random(40) . '.' . $ext;
$np = $basePath . '/' . $newFile;
Storage::move($og, $np);
$media = new Media;
$media->profile_id = $pid;
$media->user_id = $id;
$media->status_id = $status->id;
$media->media_path = $np;
$media->mime = $mime;
$media->size = $size;
$media->save();
}
$ip->status_id = $status->id;
$ip->creation_id = $idk['incr'];
$ip->save();
$profile->status_count = $profile->status_count + 1;
$profile->save();
AccountService::del($profile->id);
ImportService::clearAttempts($profile->id);
ImportService::getPostCount($profile->id, true);
}
}
}

Wyświetl plik

@ -0,0 +1,123 @@
<?php
namespace App\Console\Commands;
use App\Instance;
use App\Profile;
use App\Transformer\ActivityPub\Verb\DeleteActor;
use App\User;
use App\Util\ActivityPub\HttpSignature;
use GuzzleHttp\Client;
use GuzzleHttp\Pool;
use Illuminate\Console\Command;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\search;
use function Laravel\Prompts\table;
class UserAccountDelete extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:user-account-delete';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Federate Account Deletion';
/**
* Execute the console command.
*/
public function handle()
{
$id = search(
label: 'Search for the account to delete by username',
placeholder: 'john.appleseed',
options: fn (string $value) => strlen($value) > 0
? User::withTrashed()->whereStatus('deleted')->where('username', 'like', "%{$value}%")->pluck('username', 'id')->all()
: [],
);
$user = User::withTrashed()->find($id);
table(
['Username', 'Name', 'Email', 'Created'],
[[$user->username, $user->name, $user->email, $user->created_at]]
);
$confirmed = confirm(
label: 'Do you want to federate this account deletion?',
default: false,
yes: 'Proceed',
no: 'Cancel',
hint: 'This action is irreversible'
);
if (! $confirmed) {
$this->error('Aborting...');
exit;
}
$profile = Profile::withTrashed()->find($user->profile_id);
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($profile, new DeleteActor());
$activity = $fractal->createData($resource)->toArray();
$audience = Instance::whereNotNull(['shared_inbox', 'nodeinfo_last_fetched'])
->where('nodeinfo_last_fetched', '>', now()->subHours(12))
->distinct()
->pluck('shared_inbox');
$payload = json_encode($activity);
$client = new Client([
'timeout' => 10,
]);
$version = config('pixelfed.version');
$appUrl = config('app.url');
$userAgent = "(Pixelfed/{$version}; +{$appUrl})";
$requests = function ($audience) use ($client, $activity, $profile, $payload, $userAgent) {
foreach ($audience as $url) {
$headers = HttpSignature::sign($profile, $url, $activity, [
'Content-Type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'User-Agent' => $userAgent,
]);
yield function () use ($client, $url, $headers, $payload) {
return $client->postAsync($url, [
'curl' => [
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HEADER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
],
]);
};
}
};
$pool = new Pool($client, $requests($audience), [
'concurrency' => 50,
'fulfilled' => function ($response, $index) {
},
'rejected' => function ($reason, $index) {
},
]);
$promise = $pool->promise();
$promise->wait();
}
}

Wyświetl plik

@ -3,16 +3,17 @@
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use App\User;
class UserAdmin extends Command
class UserAdmin extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:admin {id}';
protected $signature = 'user:admin {username}';
/**
* The console command description.
@ -22,13 +23,15 @@ class UserAdmin extends Command
protected $description = 'Make a user an admin, or remove admin privileges.';
/**
* Create a new command instance.
* Prompt for missing input arguments using the returned questions.
*
* @return void
* @return array
*/
public function __construct()
protected function promptForMissingArgumentsUsing()
{
parent::__construct();
return [
'username' => 'Which username should we toggle admin privileges for?',
];
}
/**
@ -38,16 +41,15 @@ class UserAdmin extends Command
*/
public function handle()
{
$id = $this->argument('id');
if(ctype_digit($id) == true) {
$user = User::find($id);
} else {
$user = User::whereUsername($id)->first();
}
$id = $this->argument('username');
$user = User::whereUsername($id)->first();
if(!$user) {
$this->error('Could not find any user with that username or id.');
exit;
}
$this->info('Found username: ' . $user->username);
$state = $user->is_admin ? 'Remove admin privileges from this user?' : 'Add admin privileges to this user?';
$confirmed = $this->confirm($state);

Wyświetl plik

@ -52,7 +52,7 @@ class UserCreate extends Command
$user->name = $o['name'];
$user->email = $o['email'];
$user->password = bcrypt($o['password']);
$user->is_admin = (bool) $o['is_admin'];
$user->is_admin = $o['is_admin'] == 'true';
$user->email_verified_at = $o['confirm_email'] ? now() : null;
$user->save();

Wyświetl plik

@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use App\User;
class UserToggle2FA extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:2fa {username}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Disable two factor authentication for given username';
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'username' => 'Which username should we disable 2FA for?',
];
}
/**
* Execute the console command.
*/
public function handle()
{
$user = User::whereUsername($this->argument('username'))->first();
if(!$user) {
$this->error('Could not find any user with that username');
exit;
}
if(!$user->{'2fa_enabled'}) {
$this->info('User did not have 2FA enabled!');
return;
}
$user->{'2fa_enabled'} = false;
$user->{'2fa_secret'} = null;
$user->{'2fa_backup_codes'} = null;
$user->save();
$this->info('Successfully disabled 2FA on this account!');
}
}

Wyświetl plik

@ -46,7 +46,7 @@ class VideoThumbnail extends Command
->take($limit)
->get();
foreach($videos as $video) {
Pipeline::dispatchNow($video);
Pipeline::dispatch($video);
}
}
}

Wyświetl plik

@ -25,17 +25,32 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('media:optimize')->hourlyAt(40);
$schedule->command('media:gc')->hourlyAt(5);
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('story:gc')->everyFiveMinutes();
$schedule->command('gc:failedjobs')->dailyAt(3);
$schedule->command('gc:passwordreset')->dailyAt('09:41');
$schedule->command('gc:sessions')->twiceDaily(13, 23);
$schedule->command('media:optimize')->hourlyAt(40)->onOneServer();
$schedule->command('media:gc')->hourlyAt(5)->onOneServer();
$schedule->command('horizon:snapshot')->everyFiveMinutes()->onOneServer();
$schedule->command('story:gc')->everyFiveMinutes()->onOneServer();
$schedule->command('gc:failedjobs')->dailyAt(3)->onOneServer();
$schedule->command('gc:passwordreset')->dailyAt('09:41')->onOneServer();
$schedule->command('gc:sessions')->twiceDaily(13, 23)->onOneServer();
if(config('pixelfed.cloud_storage') && config('media.delete_local_after_cloud')) {
if ((bool) config_cache('pixelfed.cloud_storage') && (bool) config_cache('media.delete_local_after_cloud')) {
$schedule->command('media:s3gc')->hourlyAt(15);
}
if (config('import.instagram.enabled')) {
$schedule->command('app:transform-imports')->everyTenMinutes()->onOneServer();
$schedule->command('app:import-upload-garbage-collection')->hourlyAt(51)->onOneServer();
$schedule->command('app:import-remove-deleted-accounts')->hourlyAt(37)->onOneServer();
$schedule->command('app:import-upload-clean-storage')->twiceDailyAt(1, 13, 32)->onOneServer();
if (config('import.instagram.storage.cloud.enabled') && (bool) config_cache('pixelfed.cloud_storage')) {
$schedule->command('app:import-upload-media-to-cloud-storage')->hourlyAt(39)->onOneServer();
}
}
$schedule->command('app:notification-epoch-update')->weeklyOn(1, '2:21')->onOneServer();
$schedule->command('app:hashtag-cached-count-update')->hourlyAt(25)->onOneServer();
$schedule->command('app:account-post-count-stat-update')->everySixHours(25)->onOneServer();
}
/**
@ -45,7 +60,7 @@ class Kernel extends ConsoleKernel
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}

Wyświetl plik

@ -31,20 +31,4 @@ class DirectMessage extends Model
{
return Auth::user()->profile->id === $this->from_id;
}
public function toText()
{
$actorName = $this->author->username;
return "{$actorName} sent a direct message.";
}
public function toHtml()
{
$actorName = $this->author->username;
$actorUrl = $this->author->url();
$url = $this->url();
return "{$actorName} sent a direct message.";
}
}

Wyświetl plik

@ -32,20 +32,4 @@ class Follower extends Model
$path = $this->actor->permalink("#accepts/follows/{$this->id}{$append}");
return url($path);
}
public function toText()
{
$actorName = $this->actor->username;
return "{$actorName} ".__('notification.startedFollowingYou');
}
public function toHtml()
{
$actorName = $this->actor->username;
$actorUrl = $this->actor->url();
return "<a href='{$actorUrl}' class='profile-link'>{$actorName}</a> ".
__('notification.startedFollowingYou');
}
}

Wyświetl plik

@ -12,6 +12,8 @@ class HashtagFollow extends Model
'hashtag_id'
];
const MAX_LIMIT = 250;
public function hashtag()
{
return $this->belongsTo(Hashtag::class);

Wyświetl plik

@ -30,6 +30,7 @@ use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Transformer\Api\Mastodon\v1\AccountTransformer;
use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\NotificationService;
use App\Services\UserFilterService;
use App\Services\RelationshipService;
@ -156,7 +157,7 @@ class AccountController extends Controller
$pid = $request->user()->profile_id;
$count = UserFilterService::muteCount($pid);
$maxLimit = intval(config('instance.user_filters.max_user_mutes'));
$maxLimit = (int) config_cache('instance.user_filters.max_user_mutes');
abort_if($count >= $maxLimit, 422, self::FILTER_LIMIT_MUTE_TEXT . $maxLimit . ' accounts');
if($count == 0) {
$filterCount = UserFilter::whereUserId($pid)->count();
@ -259,7 +260,7 @@ class AccountController extends Controller
]);
$pid = $request->user()->profile_id;
$count = UserFilterService::blockCount($pid);
$maxLimit = intval(config('instance.user_filters.max_user_blocks'));
$maxLimit = (int) config_cache('instance.user_filters.max_user_blocks');
abort_if($count >= $maxLimit, 422, self::FILTER_LIMIT_BLOCK_TEXT . $maxLimit . ' accounts');
if($count == 0) {
$filterCount = UserFilter::whereUserId($pid)->whereFilterType('block')->count();
@ -282,14 +283,39 @@ class AccountController extends Controller
$filterable['id'] = $profile->id;
$filterable['type'] = $class;
Follower::whereProfileId($profile->id)->whereFollowingId($pid)->delete();
$followed = Follower::whereProfileId($profile->id)->whereFollowingId($pid)->first();
if($followed) {
$followed->delete();
$profile->following_count = Follower::whereProfileId($profile->id)->count();
$profile->save();
$selfProfile = $request->user()->profile;
$selfProfile->followers_count = Follower::whereFollowingId($pid)->count();
$selfProfile->save();
FollowerService::remove($profile->id, $pid);
AccountService::del($pid);
AccountService::del($profile->id);
}
$following = Follower::whereProfileId($pid)->whereFollowingId($profile->id)->first();
if($following) {
$following->delete();
$profile->followers_count = Follower::whereFollowingId($profile->id)->count();
$profile->save();
$selfProfile = $request->user()->profile;
$selfProfile->following_count = Follower::whereProfileId($pid)->count();
$selfProfile->save();
FollowerService::remove($pid, $profile->pid);
AccountService::del($pid);
AccountService::del($profile->id);
}
Notification::whereProfileId($pid)
->whereActorId($profile->id)
->get()
->map(function($n) use($pid) {
NotificationService::del($pid, $n['id']);
$n->forceDelete();
});
});
break;
}
@ -349,8 +375,8 @@ class AccountController extends Controller
->first();
if($filter) {
UserFilterService::unblock($pid, $filterable['id']);
$filter->delete();
UserFilterService::unblock($pid, $filterable['id']);
}
$res = RelationshipService::refresh($pid, $profile->id);

Wyświetl plik

@ -0,0 +1,255 @@
<?php
namespace App\Http\Controllers\Admin;
use DB, Cache;
use App\{
AccountInterstitial,
DiscoverCategory,
DiscoverCategoryHashtag,
Hashtag,
Media,
Profile,
Status,
StatusHashtag,
User
};
use App\Models\ConfigCache;
use App\Models\AutospamCustomTokens;
use App\Services\AccountService;
use App\Services\ConfigCacheService;
use App\Services\StatusService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use League\ISO3166\ISO3166;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\PixelfedDirectoryController;
use \DateInterval;
use \DatePeriod;
use App\Http\Resources\AdminSpamReport;
use App\Util\Lexer\Classifier;
use App\Jobs\AutospamPipeline\AutospamPretrainPipeline;
use App\Jobs\AutospamPipeline\AutospamPretrainNonSpamPipeline;
use App\Jobs\AutospamPipeline\AutospamUpdateCachedDataPipeline;
use Illuminate\Support\Facades\URL;
use App\Services\AutospamService;
trait AdminAutospamController
{
public function autospamHome(Request $request)
{
return view('admin.autospam.home');
}
public function getAutospamConfigApi(Request $request)
{
$open = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
});
$closed = Cache::remember('admin-dash:reports:spam-count-closed', 3600, function() {
return AccountInterstitial::whereType('post.autospam')->whereNotNull('appeal_handled_at')->count();
});
$thisWeek = Cache::remember('admin-dash:reports:spam-count-stats-this-week ', 86400, function() {
$sr = config('database.default') == 'pgsql' ? "to_char(created_at, 'MM-YYYY')" : "DATE_FORMAT(created_at, '%m-%Y')";
$gb = config('database.default') == 'pgsql' ? [DB::raw($sr)] : DB::raw($sr);
$s = AccountInterstitial::select(
DB::raw('count(id) as count'),
DB::raw($sr . " as month_year")
)
->where('created_at', '>=', now()->subWeeks(52))
->groupBy($gb)
->get()
->map(function($s) {
$dt = now()->parse('01-' . $s->month_year);
return [
'id' => $dt->format('Ym'),
'x' => $dt->format('M Y'),
'y' => $s->count
];
})
->sortBy('id')
->values()
->toArray();
return $s;
});
$files = [
'spam' => [
'exists' => Storage::exists(AutospamService::MODEL_SPAM_PATH),
'size' => 0
],
'ham' => [
'exists' => Storage::exists(AutospamService::MODEL_HAM_PATH),
'size' => 0
],
'combined' => [
'exists' => Storage::exists(AutospamService::MODEL_FILE_PATH),
'size' => 0
]
];
if($files['spam']['exists']) {
$files['spam']['size'] = Storage::size(AutospamService::MODEL_SPAM_PATH);
}
if($files['ham']['exists']) {
$files['ham']['size'] = Storage::size(AutospamService::MODEL_HAM_PATH);
}
if($files['combined']['exists']) {
$files['combined']['size'] = Storage::size(AutospamService::MODEL_FILE_PATH);
}
return [
'autospam_enabled' => (bool) config_cache('pixelfed.bouncer.enabled') ?? false,
'nlp_enabled' => (bool) AutospamService::active(),
'files' => $files,
'open' => $open,
'closed' => $closed,
'graph' => collect($thisWeek)->map(fn($s) => $s['y'])->values(),
'graphLabels' => collect($thisWeek)->map(fn($s) => $s['x'])->values()
];
}
public function getAutospamReportsClosedApi(Request $request)
{
$appeals = AdminSpamReport::collection(
AccountInterstitial::orderBy('id', 'desc')
->whereType('post.autospam')
->whereIsSpam(true)
->whereNotNull('appeal_handled_at')
->cursorPaginate(6)
->withQueryString()
);
return $appeals;
}
public function postAutospamTrainSpamApi(Request $request)
{
$aiCount = AccountInterstitial::whereItemType('App\Status')
->whereIsSpam(true)
->count();
abort_if($aiCount < 100, 422, 'You don\'t have enough data to pre-train against.');
$existing = Cache::get('pf:admin:autospam:pretrain:recent');
abort_if($existing, 422, 'You\'ve already run this recently, please wait 30 minutes before pre-training again');
AutospamPretrainPipeline::dispatch();
Cache::put('pf:admin:autospam:pretrain:recent', 1, 1440);
return [
'msg' => 'Success!'
];
}
public function postAutospamTrainNonSpamSearchApi(Request $request)
{
$this->validate($request, [
'q' => 'required|string|min:1'
]);
$q = $request->input('q');
$res = Profile::whereNull(['status', 'domain'])
->where('username', 'like', '%' . $q . '%')
->orderByDesc('followers_count')
->take(10)
->get()
->map(function($p) {
$acct = AccountService::get($p->id, true);
return [
'id' => (string) $p->id,
'avatar' => $acct['avatar'],
'username' => $p->username
];
})
->values();
return $res;
}
public function postAutospamTrainNonSpamSubmitApi(Request $request)
{
$this->validate($request, [
'accounts' => 'required|array|min:1|max:10'
]);
$accts = $request->input('accounts');
$accounts = Profile::whereNull(['domain', 'status'])->find(collect($accts)->map(function($a) { return $a['id'];}));
abort_if(!$accounts || !$accounts->count(), 422, 'One or more of the selected accounts are not valid');
AutospamPretrainNonSpamPipeline::dispatch($accounts);
return $accounts;
}
public function getAutospamCustomTokensApi(Request $request)
{
return AutospamCustomTokens::latest()->cursorPaginate(6);
}
public function saveNewAutospamCustomTokensApi(Request $request)
{
$this->validate($request, [
'token' => 'required|unique:autospam_custom_tokens,token',
]);
$ct = new AutospamCustomTokens;
$ct->token = $request->input('token');
$ct->weight = $request->input('weight');
$ct->category = $request->input('category') === 'spam' ? 'spam' : 'ham';
$ct->note = $request->input('note');
$ct->active = $request->input('active');
$ct->save();
AutospamUpdateCachedDataPipeline::dispatch();
return $ct;
}
public function updateAutospamCustomTokensApi(Request $request)
{
$this->validate($request, [
'id' => 'required',
'token' => 'required',
'category' => 'required|in:spam,ham',
'active' => 'required|boolean'
]);
$ct = AutospamCustomTokens::findOrFail($request->input('id'));
$ct->weight = $request->input('weight');
$ct->category = $request->input('category');
$ct->note = $request->input('note');
$ct->active = $request->input('active');
$ct->save();
AutospamUpdateCachedDataPipeline::dispatch();
return $ct;
}
public function exportAutospamCustomTokensApi(Request $request)
{
abort_if(!Storage::exists(AutospamService::MODEL_SPAM_PATH), 422, 'Autospam Dataset does not exist, please train spam before attempting to export');
return Storage::download(AutospamService::MODEL_SPAM_PATH);
}
public function enableAutospamApi(Request $request)
{
ConfigCacheService::put('autospam.nlp.enabled', true);
Cache::forget(AutospamService::CHCKD_CACHE_KEY);
return ['msg' => 'Success'];
}
public function disableAutospamApi(Request $request)
{
ConfigCacheService::put('autospam.nlp.enabled', false);
Cache::forget(AutospamService::CHCKD_CACHE_KEY);
return ['msg' => 'Success'];
}
}

Wyświetl plik

@ -2,30 +2,20 @@
namespace App\Http\Controllers\Admin;
use DB, Cache;
use App\{
DiscoverCategory,
DiscoverCategoryHashtag,
Hashtag,
Media,
Profile,
Status,
StatusHashtag,
User
};
use App\Http\Controllers\PixelfedDirectoryController;
use App\Models\ConfigCache;
use App\Services\AccountService;
use App\Services\ConfigCacheService;
use App\Services\StatusService;
use Carbon\Carbon;
use App\Status;
use App\User;
use Cache;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use League\ISO3166\ISO3166;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\PixelfedDirectoryController;
use Illuminate\Support\Str;
use League\ISO3166\ISO3166;
trait AdminDirectoryController
{
@ -41,40 +31,41 @@ trait AdminDirectoryController
$res['countries'] = collect((new ISO3166)->all())->pluck('name');
$res['admins'] = User::whereIsAdmin(true)
->where('2fa_enabled', true)
->get()->map(function($user) {
return [
'uid' => (string) $user->id,
'pid' => (string) $user->profile_id,
'username' => $user->username,
'created_at' => $user->created_at
];
});
->get()->map(function ($user) {
return [
'uid' => (string) $user->id,
'pid' => (string) $user->profile_id,
'username' => $user->username,
'created_at' => $user->created_at,
];
});
$config = ConfigCache::whereK('pixelfed.directory')->first();
if($config) {
if ($config) {
$data = $config->v ? json_decode($config->v, true) : [];
$res = array_merge($res, $data);
}
if(empty($res['summary'])) {
if (empty($res['summary'])) {
$summary = ConfigCache::whereK('app.short_description')->pluck('v');
$res['summary'] = $summary ? $summary[0] : null;
}
if(isset($res['banner_image']) && !empty($res['banner_image'])) {
if (isset($res['banner_image']) && ! empty($res['banner_image'])) {
$res['banner_image'] = url(Storage::url($res['banner_image']));
}
if(isset($res['favourite_posts'])) {
$res['favourite_posts'] = collect($res['favourite_posts'])->map(function($id) {
if (isset($res['favourite_posts'])) {
$res['favourite_posts'] = collect($res['favourite_posts'])->map(function ($id) {
return StatusService::get($id);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
}
$res['community_guidelines'] = config_cache('app.rules') ? json_decode(config_cache('app.rules'), true) : [];
$res['curated_onboarding'] = (bool) config_cache('instance.curated_registration.enabled');
$res['open_registration'] = (bool) config_cache('pixelfed.open_registration');
$res['oauth_enabled'] = (bool) config_cache('pixelfed.oauth_enabled') && file_exists(storage_path('oauth-public.key')) && file_exists(storage_path('oauth-private.key'));
@ -83,22 +74,22 @@ trait AdminDirectoryController
$res['feature_config'] = [
'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','),
'image_quality' => config_cache('pixelfed.image_quality'),
'optimize_image' => config_cache('pixelfed.optimize_image'),
'optimize_image' => (bool) config_cache('pixelfed.optimize_image'),
'max_photo_size' => config_cache('pixelfed.max_photo_size'),
'max_caption_length' => config_cache('pixelfed.max_caption_length'),
'max_altext_length' => config_cache('pixelfed.max_altext_length'),
'enforce_account_limit' => config_cache('pixelfed.enforce_account_limit'),
'enforce_account_limit' => (bool) config_cache('pixelfed.enforce_account_limit'),
'max_account_size' => config_cache('pixelfed.max_account_size'),
'max_album_length' => config_cache('pixelfed.max_album_length'),
'account_deletion' => config_cache('pixelfed.account_deletion'),
'account_deletion' => (bool) config_cache('pixelfed.account_deletion'),
];
if(config_cache('pixelfed.directory.testimonials')) {
$testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'),true))
->map(function($t) {
if (config_cache('pixelfed.directory.testimonials')) {
$testimonials = collect(json_decode(config_cache('pixelfed.directory.testimonials'), true))
->map(function ($t) {
return [
'profile' => AccountService::get($t['profile_id']),
'body' => $t['body']
'body' => $t['body'],
];
});
$res['testimonials'] = $testimonials;
@ -107,8 +98,8 @@ trait AdminDirectoryController
$validator = Validator::make($res['feature_config'], [
'media_types' => [
'required',
function ($attribute, $value, $fail) {
if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) {
function ($attribute, $value, $fail) {
if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) {
$fail('You must enable image/jpeg and image/png support.');
}
},
@ -119,12 +110,12 @@ trait AdminDirectoryController
'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000',
'max_album_length' => 'required|integer|min:4|max:20',
'account_deletion' => 'required|accepted',
'max_caption_length' => 'required|integer|min:500|max:10000'
'max_caption_length' => 'required|integer|min:500|max:10000',
]);
$res['requirements_validator'] = $validator->errors();
$res['is_eligible'] = $res['open_registration'] &&
$res['is_eligible'] = ($res['open_registration'] || $res['curated_onboarding']) &&
$res['oauth_enabled'] &&
$res['activitypub_enabled'] &&
count($res['requirements_validator']) === 0 &&
@ -145,11 +136,11 @@ trait AdminDirectoryController
foreach (new \DirectoryIterator($path) as $io) {
$name = $io->getFilename();
$skip = ['vendor'];
if($io->isDot() || in_array($name, $skip)) {
if ($io->isDot() || in_array($name, $skip)) {
continue;
}
if($io->isDir()) {
if ($io->isDir()) {
$langs->push(['code' => $name, 'name' => locale_get_display_name($name)]);
}
}
@ -158,25 +149,26 @@ trait AdminDirectoryController
$res['primary_locale'] = config('app.locale');
$submissionState = Http::withoutVerifying()
->post('https://pixelfed.org/api/v1/directory/check-submission', [
'domain' => config('pixelfed.domain.app')
]);
->post('https://pixelfed.org/api/v1/directory/check-submission', [
'domain' => config('pixelfed.domain.app'),
]);
$res['submission_state'] = $submissionState->json();
return $res;
}
protected function validVal($res, $val, $count = false, $minLen = false)
{
if(!isset($res[$val])) {
if (! isset($res[$val])) {
return false;
}
if($count) {
if ($count) {
return count($res[$val]) >= $count;
}
if($minLen) {
if ($minLen) {
return strlen($res[$val]) >= $minLen;
}
@ -193,11 +185,11 @@ trait AdminDirectoryController
'favourite_posts' => 'array|max:12',
'favourite_posts.*' => 'distinct',
'privacy_pledge' => 'sometimes',
'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000'
'banner_image' => 'sometimes|mimes:jpg,png|dimensions:width=1920,height:1080|max:5000',
]);
$config = ConfigCache::firstOrNew([
'k' => 'pixelfed.directory'
'k' => 'pixelfed.directory',
]);
$res = $config->v ? json_decode($config->v, true) : [];
@ -207,27 +199,28 @@ trait AdminDirectoryController
$res['contact_email'] = $request->input('contact_email');
$res['privacy_pledge'] = (bool) $request->input('privacy_pledge');
if($request->filled('location')) {
if ($request->filled('location')) {
$exists = (new ISO3166)->name($request->location);
if($exists) {
if ($exists) {
$res['location'] = $request->input('location');
}
}
if($request->hasFile('banner_image')) {
if ($request->hasFile('banner_image')) {
collect(Storage::files('public/headers'))
->filter(function($name) {
$protected = [
'public/headers/.gitignore',
'public/headers/default.jpg',
'public/headers/missing.png'
];
return !in_array($name, $protected);
})
->each(function($name) {
Storage::delete($name);
});
$path = $request->file('banner_image')->store('public/headers');
->filter(function ($name) {
$protected = [
'public/headers/.gitignore',
'public/headers/default.jpg',
'public/headers/missing.png',
];
return ! in_array($name, $protected);
})
->each(function ($name) {
Storage::delete($name);
});
$path = $request->file('banner_image')->storePublicly('public/headers');
$res['banner_image'] = $path;
ConfigCacheService::put('app.banner_image', url(Storage::url($path)));
@ -239,9 +232,10 @@ trait AdminDirectoryController
ConfigCacheService::put('pixelfed.directory', $config->v);
$updated = json_decode($config->v, true);
if(isset($updated['banner_image'])) {
if (isset($updated['banner_image'])) {
$updated['banner_image'] = url(Storage::url($updated['banner_image']));
}
return $updated;
}
@ -249,9 +243,10 @@ trait AdminDirectoryController
{
$reqs = [];
$reqs['feature_config'] = [
'open_registration' => config_cache('pixelfed.open_registration'),
'open_registration' => (bool) config_cache('pixelfed.open_registration'),
'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'),
'activitypub_enabled' => config_cache('federation.activitypub.enabled'),
'oauth_enabled' => config_cache('pixelfed.oauth_enabled'),
'oauth_enabled' => (bool) config_cache('pixelfed.oauth_enabled'),
'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','),
'image_quality' => config_cache('pixelfed.image_quality'),
'optimize_image' => config_cache('pixelfed.optimize_image'),
@ -265,13 +260,14 @@ trait AdminDirectoryController
];
$validator = Validator::make($reqs['feature_config'], [
'open_registration' => 'required|accepted',
'open_registration' => 'required_unless:curated_onboarding,true',
'curated_onboarding' => 'required_unless:open_registration,true',
'activitypub_enabled' => 'required|accepted',
'oauth_enabled' => 'required|accepted',
'media_types' => [
'required',
function ($attribute, $value, $fail) {
if (!in_array('image/jpeg', $value->toArray()) || !in_array('image/png', $value->toArray())) {
function ($attribute, $value, $fail) {
if (! in_array('image/jpeg', $value->toArray()) || ! in_array('image/png', $value->toArray())) {
$fail('You must enable image/jpeg and image/png support.');
}
},
@ -282,10 +278,10 @@ trait AdminDirectoryController
'max_account_size' => 'required_if:enforce_account_limit,true|integer|min:1000000',
'max_album_length' => 'required|integer|min:4|max:20',
'account_deletion' => 'required|accepted',
'max_caption_length' => 'required|integer|min:500|max:10000'
'max_caption_length' => 'required|integer|min:500|max:10000',
]);
if(!$validator->validate()) {
if (! $validator->validate()) {
return response()->json($validator->errors(), 422);
}
@ -294,6 +290,7 @@ trait AdminDirectoryController
$data = (new PixelfedDirectoryController())->buildListing();
$res = Http::withoutVerifying()->post('https://pixelfed.org/api/v1/directory/submission', $data);
return 200;
}
@ -301,7 +298,7 @@ trait AdminDirectoryController
{
$bannerImage = ConfigCache::whereK('app.banner_image')->first();
$directory = ConfigCache::whereK('pixelfed.directory')->first();
if(!$bannerImage && !$directory || empty($directory->v)) {
if (! $bannerImage && ! $directory || empty($directory->v)) {
return;
}
$directoryArr = json_decode($directory->v, true);
@ -309,12 +306,12 @@ trait AdminDirectoryController
$protected = [
'public/headers/.gitignore',
'public/headers/default.jpg',
'public/headers/missing.png'
'public/headers/missing.png',
];
if(!$path || in_array($path, $protected)) {
if (! $path || in_array($path, $protected)) {
return;
}
if(Storage::exists($directoryArr['banner_image'])) {
if (Storage::exists($directoryArr['banner_image'])) {
Storage::delete($directoryArr['banner_image']);
}
@ -325,12 +322,13 @@ trait AdminDirectoryController
$bannerImage->save();
Cache::forget('api:v1:instance-data-response-v1');
ConfigCacheService::put('pixelfed.directory', $directory);
return $bannerImage->v;
}
public function directoryGetPopularPosts(Request $request)
{
$ids = Cache::remember('admin:api:popular_posts', 86400, function() {
$ids = Cache::remember('admin:api:popular_posts', 86400, function () {
return Status::whereLocal(true)
->whereScope('public')
->whereType('photo')
@ -340,21 +338,21 @@ trait AdminDirectoryController
->pluck('id');
});
$res = $ids->map(function($id) {
$res = $ids->map(function ($id) {
return StatusService::get($id);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function directoryGetAddPostByIdSearch(Request $request)
{
$this->validate($request, [
'q' => 'required|integer'
'q' => 'required|integer',
]);
$id = $request->input('q');
@ -377,11 +375,12 @@ trait AdminDirectoryController
$profile_id = $request->input('profile_id');
$testimonials = ConfigCache::whereK('pixelfed.directory.testimonials')->firstOrFail();
$existing = collect(json_decode($testimonials->v, true))
->filter(function($t) use($profile_id) {
->filter(function ($t) use ($profile_id) {
return $t['profile_id'] !== $profile_id;
})
->values();
ConfigCacheService::put('pixelfed.directory.testimonials', $existing);
return $existing;
}
@ -389,13 +388,13 @@ trait AdminDirectoryController
{
$this->validate($request, [
'username' => 'required',
'body' => 'required|string|min:5|max:500'
'body' => 'required|string|min:5|max:500',
]);
$user = User::whereUsername($request->input('username'))->whereNull('status')->firstOrFail();
$configCache = ConfigCache::firstOrCreate([
'k' => 'pixelfed.directory.testimonials'
'k' => 'pixelfed.directory.testimonials',
]);
$testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]);
@ -406,7 +405,7 @@ trait AdminDirectoryController
$testimonials->push([
'profile_id' => (string) $user->profile_id,
'username' => $request->input('username'),
'body' => $request->input('body')
'body' => $request->input('body'),
]);
$configCache->v = json_encode($testimonials->toArray());
@ -414,8 +413,9 @@ trait AdminDirectoryController
ConfigCacheService::put('pixelfed.directory.testimonials', $configCache->v);
$res = [
'profile' => AccountService::get($user->profile_id),
'body' => $request->input('body')
'body' => $request->input('body'),
];
return $res;
}
@ -423,7 +423,7 @@ trait AdminDirectoryController
{
$this->validate($request, [
'profile_id' => 'required',
'body' => 'required|string|min:5|max:500'
'body' => 'required|string|min:5|max:500',
]);
$profile_id = $request->input('profile_id');
@ -431,18 +431,19 @@ trait AdminDirectoryController
$user = User::whereProfileId($profile_id)->firstOrFail();
$configCache = ConfigCache::firstOrCreate([
'k' => 'pixelfed.directory.testimonials'
'k' => 'pixelfed.directory.testimonials',
]);
$testimonials = $configCache->v ? collect(json_decode($configCache->v, true)) : collect([]);
$updated = $testimonials->map(function($t) use($profile_id, $body) {
if($t['profile_id'] == $profile_id) {
$updated = $testimonials->map(function ($t) use ($profile_id, $body) {
if ($t['profile_id'] == $profile_id) {
$t['body'] = $body;
}
return $t;
})
->values();
->values();
$configCache->v = json_encode($updated);
$configCache->save();

Wyświetl plik

@ -11,6 +11,7 @@ use App\Mail\AdminMessage;
use Illuminate\Support\Facades\Mail;
use App\Services\ModLogService;
use App\Jobs\DeletePipeline\DeleteAccountPipeline;
use App\Services\AccountService;
trait AdminUserController
{
@ -25,7 +26,7 @@ trait AdminUserController
'next' => $offset + 1,
'query' => $search ? '&a=search&q=' . $search : null
];
$users = User::select('id', 'username', 'status', 'profile_id')
$users = User::select('id', 'username', 'status', 'profile_id', 'is_admin')
->orderBy($col, $dir)
->when($search, function($q, $search) {
return $q->where('username', 'like', "%{$search}%");
@ -34,7 +35,11 @@ trait AdminUserController
return $q->offset(($offset * 10));
})
->limit(10)
->get();
->get()
->map(function($u) {
$u['account'] = AccountService::get($u->profile_id, true);
return $u;
});
return view('admin.users.home', compact('users', 'pagination'));
}

Wyświetl plik

@ -21,6 +21,7 @@ use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use App\Http\Controllers\Admin\{
AdminAutospamController,
AdminDirectoryController,
AdminDiscoverController,
AdminHashtagsController,
@ -43,6 +44,7 @@ use App\Models\CustomEmoji;
class AdminController extends Controller
{
use AdminReportController,
AdminAutospamController,
AdminDirectoryController,
AdminDiscoverController,
AdminHashtagsController,
@ -422,7 +424,7 @@ class AdminController extends Controller
public function customEmojiHome(Request $request)
{
if(!config('federation.custom_emoji.enabled')) {
if(!(bool) config_cache('federation.custom_emoji.enabled')) {
return view('admin.custom-emoji.not-enabled');
}
$this->validate($request, [
@ -464,7 +466,9 @@ class AdminController extends Controller
->where('shortcode', 'like', '%' . $request->input('q') . '%')
->orWhere('domain', 'like', '%' . $request->input('q') . '%');
if(!$request->has('dups')) {
$q = $q->groupBy('shortcode');
if(!$pg) {
$q = $q->groupBy('shortcode');
}
}
return $q;
}
@ -493,7 +497,7 @@ class AdminController extends Controller
public function customEmojiToggleActive(Request $request, $id)
{
abort_unless(config('federation.custom_emoji.enabled'), 404);
abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404);
$emoji = CustomEmoji::findOrFail($id);
$emoji->disabled = !$emoji->disabled;
$emoji->save();
@ -504,13 +508,13 @@ class AdminController extends Controller
public function customEmojiAdd(Request $request)
{
abort_unless(config('federation.custom_emoji.enabled'), 404);
abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404);
return view('admin.custom-emoji.add');
}
public function customEmojiStore(Request $request)
{
abort_unless(config('federation.custom_emoji.enabled'), 404);
abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404);
$this->validate($request, [
'shortcode' => [
'required',
@ -532,7 +536,7 @@ class AdminController extends Controller
$emoji->save();
$fileName = $emoji->id . '.' . $request->emoji->extension();
$request->emoji->storeAs('public/emoji', $fileName);
$request->emoji->storePubliclyAs('public/emoji', $fileName);
$emoji->media_path = 'emoji/' . $fileName;
$emoji->save();
Cache::forget('pf:custom_emoji');
@ -541,7 +545,7 @@ class AdminController extends Controller
public function customEmojiDelete(Request $request, $id)
{
abort_unless(config('federation.custom_emoji.enabled'), 404);
abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404);
$emoji = CustomEmoji::findOrFail($id);
Storage::delete("public/{$emoji->media_path}");
Cache::forget('pf:custom_emoji');
@ -551,7 +555,7 @@ class AdminController extends Controller
public function customEmojiShowDuplicates(Request $request, $id)
{
abort_unless(config('federation.custom_emoji.enabled'), 404);
abort_unless((bool) config_cache('federation.custom_emoji.enabled'), 404);
$emoji = CustomEmoji::orderBy('id')->whereDisabled(false)->whereShortcode($id)->firstOrFail();
$emojis = CustomEmoji::whereShortcode($id)->where('id', '!=', $emoji->id)->cursorPaginate(10);
return view('admin.custom-emoji.duplicates', compact('emoji', 'emojis'));

Wyświetl plik

@ -0,0 +1,335 @@
<?php
namespace App\Http\Controllers;
use App\Mail\CuratedRegisterAcceptUser;
use App\Mail\CuratedRegisterRejectUser;
use App\Mail\CuratedRegisterRequestDetailsFromUser;
use App\Models\CuratedRegister;
use App\Models\CuratedRegisterActivity;
use App\Models\CuratedRegisterTemplate;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class AdminCuratedRegisterController extends Controller
{
public function __construct()
{
$this->middleware(['auth', 'admin']);
}
public function index(Request $request)
{
$this->validate($request, [
'filter' => 'sometimes|in:open,all,awaiting,approved,rejected,responses',
'sort' => 'sometimes|in:asc,desc',
]);
$filter = $request->input('filter', 'open');
$sort = $request->input('sort', 'asc');
$records = CuratedRegister::when($filter, function ($q, $filter) {
if ($filter === 'open') {
return $q->where('is_rejected', false)
->where(function ($query) {
return $query->where('user_has_responded', true)->orWhere('is_awaiting_more_info', false);
})
->whereNotNull('email_verified_at')
->whereIsClosed(false);
} elseif ($filter === 'all') {
return $q;
} elseif ($filter === 'responses') {
return $q->whereIsClosed(false)
->whereNotNull('email_verified_at')
->where('user_has_responded', true)
->where('is_awaiting_more_info', true);
} elseif ($filter === 'awaiting') {
return $q->whereIsClosed(false)
->where('is_rejected', false)
->where('is_approved', false)
->where('user_has_responded', false)
->where('is_awaiting_more_info', true);
} elseif ($filter === 'approved') {
return $q->whereIsClosed(true)->whereIsApproved(true);
} elseif ($filter === 'rejected') {
return $q->whereIsClosed(true)->whereIsRejected(true);
}
})
->when($sort, function ($query, $sort) {
return $query->orderBy('id', $sort);
})
->paginate(10)
->withQueryString();
return view('admin.curated-register.index', compact('records', 'filter'));
}
public function show(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
return view('admin.curated-register.show', compact('record'));
}
public function apiActivityLog(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
$res = collect([
[
'id' => 1,
'action' => 'created',
'title' => 'Onboarding application created',
'message' => null,
'link' => null,
'timestamp' => $record->created_at,
],
]);
if ($record->email_verified_at) {
$res->push([
'id' => 3,
'action' => 'email_verified_at',
'title' => 'Applicant successfully verified email address',
'message' => null,
'link' => null,
'timestamp' => $record->email_verified_at,
]);
}
$activities = CuratedRegisterActivity::whereRegisterId($record->id)->get();
$idx = 4;
$userResponses = collect([]);
foreach ($activities as $activity) {
$idx++;
if ($activity->type === 'user_resend_email_confirmation') {
continue;
}
if ($activity->from_user) {
$userResponses->push($activity);
continue;
}
$res->push([
'id' => $idx,
'aid' => $activity->id,
'action' => $activity->type,
'title' => $activity->from_admin ? 'Admin requested info' : 'User responded',
'message' => $activity->message,
'link' => $activity->adminReviewUrl(),
'timestamp' => $activity->created_at,
]);
}
foreach ($userResponses as $ur) {
$res = $res->map(function ($r) use ($ur) {
if (! isset($r['aid'])) {
return $r;
}
if ($ur->reply_to_id === $r['aid']) {
$r['user_response'] = $ur;
return $r;
}
return $r;
});
}
if ($record->is_approved) {
$idx++;
$res->push([
'id' => $idx,
'action' => 'approved',
'title' => 'Application Approved',
'message' => null,
'link' => null,
'timestamp' => $record->action_taken_at,
]);
} elseif ($record->is_rejected) {
$idx++;
$res->push([
'id' => $idx,
'action' => 'rejected',
'title' => 'Application Rejected',
'message' => null,
'link' => null,
'timestamp' => $record->action_taken_at,
]);
}
return $res->reverse()->values();
}
public function apiMessagePreviewStore(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
return $request->all();
}
public function apiMessageSendStore(Request $request, $id)
{
$this->validate($request, [
'message' => 'required|string|min:5|max:3000',
]);
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$activity = new CuratedRegisterActivity;
$activity->register_id = $record->id;
$activity->admin_id = $request->user()->id;
$activity->secret_code = Str::random(32);
$activity->type = 'request_details';
$activity->from_admin = true;
$activity->message = $request->input('message');
$activity->save();
$record->is_awaiting_more_info = true;
$record->user_has_responded = false;
$record->save();
Mail::to($record->email)->send(new CuratedRegisterRequestDetailsFromUser($record, $activity));
return $request->all();
}
public function previewDetailsMessageShow(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$activity = new CuratedRegisterActivity;
$activity->message = $request->input('message');
return new \App\Mail\CuratedRegisterRequestDetailsFromUser($record, $activity);
}
public function previewMessageShow(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
$record->message = $request->input('message');
return new \App\Mail\CuratedRegisterSendMessage($record);
}
public function apiHandleReject(Request $request, $id)
{
$this->validate($request, [
'action' => 'required|in:reject-email,reject-silent',
]);
$action = $request->input('action');
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
$record->is_rejected = true;
$record->is_closed = true;
$record->action_taken_at = now();
$record->save();
if ($action === 'reject-email') {
Mail::to($record->email)->send(new CuratedRegisterRejectUser($record));
}
return [200];
}
public function apiHandleApprove(Request $request, $id)
{
$record = CuratedRegister::findOrFail($id);
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
$record->is_approved = true;
$record->is_closed = true;
$record->action_taken_at = now();
$record->save();
$user = User::create([
'name' => $record->username,
'username' => $record->username,
'email' => $record->email,
'password' => $record->password,
'app_register_ip' => $record->ip_address,
'email_verified_at' => now(),
'register_source' => 'cur_onboarding',
]);
Mail::to($record->email)->send(new CuratedRegisterAcceptUser($record));
return [200];
}
public function templates(Request $request)
{
$templates = CuratedRegisterTemplate::paginate(10);
return view('admin.curated-register.templates', compact('templates'));
}
public function templateCreate(Request $request)
{
return view('admin.curated-register.template-create');
}
public function templateEdit(Request $request, $id)
{
$template = CuratedRegisterTemplate::findOrFail($id);
return view('admin.curated-register.template-edit', compact('template'));
}
public function templateEditStore(Request $request, $id)
{
$this->validate($request, [
'name' => 'required|string|max:30',
'content' => 'required|string|min:5|max:3000',
'description' => 'nullable|sometimes|string|max:1000',
'active' => 'sometimes',
]);
$template = CuratedRegisterTemplate::findOrFail($id);
$template->name = $request->input('name');
$template->content = $request->input('content');
$template->description = $request->input('description');
$template->is_active = $request->boolean('active');
$template->save();
return redirect()->back()->with('status', 'Successfully updated template!');
}
public function templateDelete(Request $request, $id)
{
$template = CuratedRegisterTemplate::findOrFail($id);
$template->delete();
return redirect(route('admin.curated-onboarding.templates'))->with('status', 'Successfully deleted template!');
}
public function templateStore(Request $request)
{
$this->validate($request, [
'name' => 'required|string|max:30',
'content' => 'required|string|min:5|max:3000',
'description' => 'nullable|sometimes|string|max:1000',
'active' => 'sometimes',
]);
CuratedRegisterTemplate::create([
'name' => $request->input('name'),
'content' => $request->input('content'),
'description' => $request->input('description'),
'is_active' => $request->boolean('active'),
]);
return redirect(route('admin.curated-onboarding.templates'))->with('status', 'Successfully created new template!');
}
public function getActiveTemplates(Request $request)
{
$templates = CuratedRegisterTemplate::whereIsActive(true)
->orderBy('order')
->get()
->map(function ($tmp) {
return [
'name' => $tmp->name,
'content' => $tmp->content,
];
});
return response()->json($templates);
}
}

Wyświetl plik

@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\AdminShadowFilter;
use App\Profile;
use App\Services\AccountService;
use App\Services\AdminShadowFilterService;
class AdminShadowFilterController extends Controller
{
public function __construct()
{
$this->middleware(['auth','admin']);
}
public function home(Request $request)
{
$filter = $request->input('filter');
$searchQuery = $request->input('q');
$filters = AdminShadowFilter::whereHas('profile')
->when($filter, function($q, $filter) {
if($filter == 'all') {
return $q;
} else if($filter == 'inactive') {
return $q->whereActive(false);
} else {
return $q;
}
}, function($q, $filter) {
return $q->whereActive(true);
})
->when($searchQuery, function($q, $searchQuery) {
$ids = Profile::where('username', 'like', '%' . $searchQuery . '%')
->limit(100)
->pluck('id')
->toArray();
return $q->where('item_type', 'App\Profile')->whereIn('item_id', $ids);
})
->latest()
->paginate(10)
->withQueryString();
return view('admin.asf.home', compact('filters'));
}
public function create(Request $request)
{
return view('admin.asf.create');
}
public function edit(Request $request, $id)
{
$filter = AdminShadowFilter::findOrFail($id);
$profile = AccountService::get($filter->item_id);
return view('admin.asf.edit', compact('filter', 'profile'));
}
public function store(Request $request)
{
$this->validate($request, [
'username' => 'required',
'active' => 'sometimes',
'note' => 'sometimes',
'hide_from_public_feeds' => 'sometimes'
]);
$profile = Profile::whereUsername($request->input('username'))->first();
if(!$profile) {
return back()->withErrors(['Invalid account']);
}
if($profile->user && $profile->user->is_admin) {
return back()->withErrors(['Cannot filter an admin account']);
}
$active = $request->has('active') && $request->has('hide_from_public_feeds');
AdminShadowFilter::updateOrCreate([
'item_id' => $profile->id,
'item_type' => get_class($profile)
], [
'is_local' => $profile->domain === null,
'note' => $request->input('note'),
'hide_from_public_feeds' => $request->has('hide_from_public_feeds'),
'admin_id' => $request->user()->profile_id,
'active' => $active
]);
AdminShadowFilterService::refresh();
return redirect('/i/admin/asf/home');
}
public function storeEdit(Request $request, $id)
{
$this->validate($request, [
'active' => 'sometimes',
'note' => 'sometimes',
'hide_from_public_feeds' => 'sometimes'
]);
$filter = AdminShadowFilter::findOrFail($id);
$profile = Profile::findOrFail($filter->item_id);
if($profile->user && $profile->user->is_admin) {
return back()->withErrors(['Cannot filter an admin account']);
}
$active = $request->has('active');
$filter->active = $active;
$filter->hide_from_public_feeds = $request->has('hide_from_public_feeds');
$filter->note = $request->input('note');
$filter->save();
AdminShadowFilterService::refresh();
return redirect('/i/admin/asf/home');
}
}

Wyświetl plik

@ -11,37 +11,49 @@ use App\{
AccountInterstitial,
Instance,
Like,
Notification,
Media,
Profile,
Report,
Status,
User
};
use App\Models\Conversation;
use App\Models\RemoteReport;
use App\Services\AccountService;
use App\Services\AdminStatsService;
use App\Services\ConfigCacheService;
use App\Services\InstanceService;
use App\Services\ModLogService;
use App\Services\SnowflakeService;
use App\Services\StatusService;
use App\Services\PublicTimelineService;
use App\Services\NetworkTimelineService;
use App\Services\NotificationService;
use App\Http\Resources\AdminInstance;
use App\Http\Resources\AdminUser;
use App\Jobs\DeletePipeline\DeleteAccountPipeline;
use App\Jobs\DeletePipeline\DeleteRemoteProfilePipeline;
use App\Jobs\DeletePipeline\DeleteRemoteStatusPipeline;
class AdminApiController extends Controller
{
public function supported(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
return response()->json(['supported' => true]);
}
public function getStats(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$res = AdminStatsService::summary();
$res['autospam_count'] = AccountInterstitial::whereType('post.autospam')
@ -52,8 +64,10 @@ class AdminApiController extends Controller
public function autospam(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$appeals = AccountInterstitial::whereType('post.autospam')
->whereNull('appeal_handled_at')
@ -87,11 +101,13 @@ class AdminApiController extends Controller
public function autospamHandle(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'action' => 'required|in:dismiss,approve,dismiss-all,approve-all',
'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-post,delete-account',
'id' => 'required'
]);
@ -103,14 +119,53 @@ class AdminApiController extends Controller
$now = now();
$res = ['status' => 'success'];
$meta = json_decode($appeal->meta);
$user = $appeal->user;
$profile = $user->profile;
if($action == 'dismiss') {
$appeal->is_spam = true;
$appeal->appeal_handled_at = $now;
$appeal->save();
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $profile->id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $profile->id);
Cache::forget('admin-dash:reports:spam-count');
return $res;
}
if($action == 'delete-post') {
$appeal->appeal_handled_at = now();
$appeal->is_spam = true;
$appeal->save();
ModLogService::boot()
->objectUid($profile->id)
->objectId($appeal->status->id)
->objectType('App\Status::class')
->user($request->user())
->action('admin.status.delete')
->accessLevel('admin')
->save();
PublicTimelineService::deleteByProfileId($profile->id);
StatusDelete::dispatch($appeal->status)->onQueue('high');
Cache::forget('admin-dash:reports:spam-count');
return $res;
}
if($action == 'delete-account') {
abort_if($user->is_admin, 400, 'Cannot delete an admin account.');
$appeal->appeal_handled_at = now();
$appeal->is_spam = true;
$appeal->save();
ModLogService::boot()
->objectUid($profile->id)
->objectId($profile->id)
->objectType('App\User::class')
->user($request->user())
->action('admin.user.delete')
->accessLevel('admin')
->save();
PublicTimelineService::deleteByProfileId($profile->id);
DeleteAccountPipeline::dispatch($appeal->user)->onQueue('high');
Cache::forget('admin-dash:reports:spam-count');
return $res;
}
@ -140,6 +195,14 @@ class AdminApiController extends Controller
StatusService::del($status->id);
Notification::whereAction('autospam.warning')
->whereProfileId($appeal->user->profile_id)
->get()
->each(function($n) use($appeal) {
NotificationService::del($appeal->user->profile_id, $n->id);
$n->forceDelete();
});
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
Cache::forget('admin-dash:reports:spam-count');
@ -164,6 +227,14 @@ class AdminApiController extends Controller
$status->save();
StatusService::del($status->id, true);
}
Notification::whereAction('autospam.warning')
->whereProfileId($report->user->profile_id)
->get()
->each(function($n) use($report) {
NotificationService::del($report->user->profile_id, $n->id);
$n->forceDelete();
});
});
Cache::forget('pf:bouncer_v0:exemption_by_pid:' . $appeal->user->profile_id);
Cache::forget('pf:bouncer_v0:recent_by_pid:' . $appeal->user->profile_id);
@ -176,8 +247,10 @@ class AdminApiController extends Controller
public function modReports(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$reports = Report::whereNull('admin_seen')
->orderBy('created_at','desc')
@ -222,8 +295,10 @@ class AdminApiController extends Controller
public function modReportHandle(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'action' => 'required|string',
@ -280,8 +355,11 @@ class AdminApiController extends Controller
public function getConfiguration(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
abort_unless(config('instance.enable_cc'), 400);
return collect([
@ -323,8 +401,11 @@ class AdminApiController extends Controller
public function updateConfiguration(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
abort_unless(config('instance.enable_cc'), 400);
$this->validate($request, [
@ -385,8 +466,14 @@ class AdminApiController extends Controller
public function getUsers(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$this->validate($request, [
'sort' => 'sometimes|in:asc,desc',
]);
$q = $request->input('q');
$sort = $request->input('sort', 'desc') === 'asc' ? 'asc' : 'desc';
$res = User::whereNull('status')
@ -400,31 +487,47 @@ class AdminApiController extends Controller
public function getUser(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$id = $request->input('user_id');
$user = User::findOrFail($id);
$profile = $user->profile;
$account = AccountService::get($user->profile_id, true);
return (new AdminUser($user))->additional(['meta' => [
'account' => $account,
'moderation' => [
'unlisted' => (bool) $profile->unlisted,
'cw' => (bool) $profile->cw,
'no_autolink' => (bool) $profile->no_autolink
]
]]);
$key = 'pf-admin-api:getUser:byId:' . $id;
if($request->has('refresh')) {
Cache::forget($key);
}
return Cache::remember($key, 86400, function() use($id) {
$user = User::findOrFail($id);
$profile = $user->profile;
$account = AccountService::get($user->profile_id, true);
$res = (new AdminUser($user))->additional(['meta' => [
'cached_at' => str_replace('+00:00', 'Z', now()->format(DATE_RFC3339_EXTENDED)),
'account' => $account,
'dms_sent' => Conversation::whereFromId($profile->id)->count(),
'report_count' => Report::where('object_id', $profile->id)->orWhere('reported_profile_id', $profile->id)->count(),
'remote_report_count' => RemoteReport::whereAccountId($profile->id)->count(),
'moderation' => [
'unlisted' => (bool) $profile->unlisted,
'cw' => (bool) $profile->cw,
'no_autolink' => (bool) $profile->no_autolink
]
]]);
return $res;
});
}
public function userAdminAction(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
'action' => 'required|in:unlisted,cw,no_autolink,refresh_stats,verify_email',
'action' => 'required|in:unlisted,cw,no_autolink,refresh_stats,verify_email,delete',
'value' => 'sometimes'
]);
@ -435,7 +538,59 @@ class AdminApiController extends Controller
abort_if($user->is_admin == true && $action !== 'refresh_stats', 400, 'Cannot moderate admin accounts');
if($action === 'refresh_stats') {
if($action === 'delete') {
if(config('pixelfed.account_deletion') == false) {
abort(404);
}
abort_if($user->is_admin, 400, 'Cannot delete an admin account.');
$ts = now()->addMonth();
$user->status = 'delete';
$user->delete_after = $ts;
$user->save();
$profile->status = 'delete';
$profile->delete_after = $ts;
$profile->save();
ModLogService::boot()
->objectUid($profile->id)
->objectId($profile->id)
->objectType('App\Profile::class')
->user($request->user())
->action('admin.user.delete')
->accessLevel('admin')
->save();
PublicTimelineService::deleteByProfileId($profile->id);
NetworkTimelineService::deleteByProfileId($profile->id);
if($profile->user_id) {
DB::table('oauth_access_tokens')->whereUserId($user->id)->delete();
DB::table('oauth_auth_codes')->whereUserId($user->id)->delete();
$user->email = $user->id;
$user->password = '';
$user->status = 'delete';
$user->save();
$profile->status = 'delete';
$profile->delete_after = now()->addMonth();
$profile->save();
AccountService::del($profile->id);
DeleteAccountPipeline::dispatch($user)->onQueue('high');
} else {
$profile->status = 'delete';
$profile->delete_after = now()->addMonth();
$profile->save();
AccountService::del($profile->id);
DeleteRemoteProfilePipeline::dispatch($profile)->onQueue('high');
}
return [
'status' => 200,
'msg' => 'deleted',
];
} else if($action === 'refresh_stats') {
$profile->following_count = DB::table('followers')->whereProfileId($user->profile_id)->count();
$profile->followers_count = DB::table('followers')->whereFollowingId($user->profile_id)->count();
$statusCount = Status::whereProfileId($user->profile_id)
@ -461,6 +616,51 @@ class AdminApiController extends Controller
])
->accessLevel('admin')
->save();
} else if($action === 'unlisted') {
ModLogService::boot()
->objectUid($profile->id)
->objectId($profile->id)
->objectType('App\Profile::class')
->user($request->user())
->action('admin.user.moderate')
->metadata([
'action' => $action,
'message' => 'Success!'
])
->accessLevel('admin')
->save();
$profile->unlisted = !$profile->unlisted;
$profile->save();
} else if($action === 'cw') {
ModLogService::boot()
->objectUid($profile->id)
->objectId($profile->id)
->objectType('App\Profile::class')
->user($request->user())
->action('admin.user.moderate')
->metadata([
'action' => $action,
'message' => 'Success!'
])
->accessLevel('admin')
->save();
$profile->cw = !$profile->cw;
$profile->save();
} else if($action === 'no_autolink') {
ModLogService::boot()
->objectUid($profile->id)
->objectId($profile->id)
->objectType('App\Profile::class')
->user($request->user())
->action('admin.user.moderate')
->metadata([
'action' => $action,
'message' => 'Success!'
])
->accessLevel('admin')
->save();
$profile->no_autolink = !$profile->no_autolink;
$profile->save();
} else {
$profile->{$action} = filter_var($request->input('value'), FILTER_VALIDATE_BOOLEAN);
$profile->save();
@ -494,8 +694,10 @@ class AdminApiController extends Controller
public function instances(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'q' => 'sometimes',
@ -532,8 +734,10 @@ class AdminApiController extends Controller
public function getInstance(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
$id = $request->input('id');
$res = Instance::findOrFail($id);
@ -543,8 +747,10 @@ class AdminApiController extends Controller
public function moderateInstance(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
@ -567,8 +773,10 @@ class AdminApiController extends Controller
public function refreshInstanceStats(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin == 1, 404);
abort_unless($request->user()->tokenCan('admin:write'), 404);
$this->validate($request, [
'id' => 'required',
@ -582,4 +790,64 @@ class AdminApiController extends Controller
return new AdminInstance($instance);
}
public function getAllStats(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 404);
abort_unless($request->user()->is_admin === 1, 404);
abort_unless($request->user()->tokenCan('admin:read'), 404);
if($request->has('refresh')) {
Cache::forget('admin-api:instance-all-stats-v1');
}
return Cache::remember('admin-api:instance-all-stats-v1', 1209600, function() {
$days = range(1, 7);
$res = [
'cached_at' => now()->format('c'),
];
$minStatusId = SnowflakeService::byDate(now()->subDays(7));
foreach($days as $day) {
$label = now()->subDays($day)->format('D');
$labelShort = substr($label, 0, 1);
$res['users']['days'][] = [
'date' => now()->subDays($day)->format('M j Y'),
'label_full' => $label,
'label' => $labelShort,
'count' => User::whereDate('created_at', now()->subDays($day))->count()
];
$res['posts']['days'][] = [
'date' => now()->subDays($day)->format('M j Y'),
'label_full' => $label,
'label' => $labelShort,
'count' => Status::whereNull('uri')->where('id', '>', $minStatusId)->whereDate('created_at', now()->subDays($day))->count()
];
$res['instances']['days'][] = [
'date' => now()->subDays($day)->format('M j Y'),
'label_full' => $label,
'label' => $labelShort,
'count' => Instance::whereDate('created_at', now()->subDays($day))->count()
];
}
$res['users']['total'] = DB::table('users')->count();
$res['users']['min'] = collect($res['users']['days'])->min('count');
$res['users']['max'] = collect($res['users']['days'])->max('count');
$res['users']['change'] = collect($res['users']['days'])->sum('count');;
$res['posts']['total'] = DB::table('statuses')->whereNull('uri')->count();
$res['posts']['min'] = collect($res['posts']['days'])->min('count');
$res['posts']['max'] = collect($res['posts']['days'])->max('count');
$res['posts']['change'] = collect($res['posts']['days'])->sum('count');
$res['instances']['total'] = DB::table('instances')->count();
$res['instances']['min'] = collect($res['instances']['days'])->min('count');
$res['instances']['max'] = collect($res['instances']['days'])->max('count');
$res['instances']['change'] = collect($res['instances']['days'])->sum('count');
return $res;
});
}
}

Wyświetl plik

@ -0,0 +1,339 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Media;
use App\UserSetting;
use App\User;
use Illuminate\Support\Facades\Cache;
use App\Services\AccountService;
use App\Services\BouncerService;
use App\Services\InstanceService;
use App\Services\MediaBlocklistService;
use App\Services\MediaPathService;
use App\Services\SearchApiV2Service;
use App\Util\Media\Filter;
use App\Jobs\MediaPipeline\MediaDeletePipeline;
use App\Jobs\VideoPipeline\{
VideoOptimize,
VideoPostProcess,
VideoThumbnail
};
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Transformer\Api\Mastodon\v1\{
AccountTransformer,
MediaTransformer,
NotificationTransformer,
StatusTransformer,
};
use App\Transformer\Api\{
RelationshipTransformer,
};
use App\Util\Site\Nodeinfo;
use App\Services\UserRoleService;
class ApiV2Controller extends Controller
{
const PF_API_ENTITY_KEY = "_pe";
public function json($res, $code = 200, $headers = [])
{
return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
}
public function instance(Request $request)
{
$contact = Cache::remember('api:v1:instance-data:contact', 604800, function () {
if(config_cache('instance.admin.pid')) {
return AccountService::getMastodon(config_cache('instance.admin.pid'), true);
}
$admin = User::whereIsAdmin(true)->first();
return $admin && isset($admin->profile_id) ?
AccountService::getMastodon($admin->profile_id, true) :
null;
});
$rules = Cache::remember('api:v1:instance-data:rules', 604800, function () {
return config_cache('app.rules') ?
collect(json_decode(config_cache('app.rules'), true))
->map(function($rule, $key) {
$id = $key + 1;
return [
'id' => "{$id}",
'text' => $rule
];
})
->toArray() : [];
});
$res = Cache::remember('api:v2:instance-data-response-v2', 1800, function () use($contact, $rules) {
return [
'domain' => config('pixelfed.domain.app'),
'title' => config_cache('app.name'),
'version' => '3.5.3 (compatible; Pixelfed ' . config('pixelfed.version') .')',
'source_url' => 'https://github.com/pixelfed/pixelfed',
'description' => config_cache('app.short_description'),
'usage' => [
'users' => [
'active_month' => (int) Nodeinfo::activeUsersMonthly()
]
],
'thumbnail' => [
'url' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'blurhash' => InstanceService::headerBlurhash(),
'versions' => [
'@1x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
'@2x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'))
]
],
'languages' => [config('app.locale')],
'configuration' => [
'urls' => [
'streaming' => null,
'status' => null
],
'vapid' => [
'public_key' => config('webpush.vapid.public_key'),
],
'accounts' => [
'max_featured_tags' => 0,
],
'statuses' => [
'max_characters' => (int) config_cache('pixelfed.max_caption_length'),
'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'),
'characters_reserved_per_url' => 23
],
'media_attachments' => [
'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')),
'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'image_matrix_limit' => 3686400,
'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
'video_frame_rate_limit' => 240,
'video_matrix_limit' => 3686400
],
'polls' => [
'max_options' => 0,
'max_characters_per_option' => 0,
'min_expiration' => 0,
'max_expiration' => 0,
],
'translation' => [
'enabled' => false,
],
],
'registrations' => [
'enabled' => null,
'approval_required' => false,
'message' => null,
'url' => null,
],
'contact' => [
'email' => config('instance.email'),
'account' => $contact
],
'rules' => $rules
];
});
$res['registrations']['enabled'] = (bool) config_cache('pixelfed.open_registration');
$res['registrations']['approval_required'] = (bool) config_cache('instance.curated_registration.enabled');
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
/**
* GET /api/v2/search
*
*
* @return array
*/
public function search(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$this->validate($request, [
'q' => 'required|string|min:1|max:100',
'account_id' => 'nullable|string',
'max_id' => 'nullable|string',
'min_id' => 'nullable|string',
'type' => 'nullable|in:accounts,hashtags,statuses',
'exclude_unreviewed' => 'nullable',
'resolve' => 'nullable',
'limit' => 'nullable|integer|max:40',
'offset' => 'nullable|integer',
'following' => 'nullable'
]);
if($request->user()->has_roles && !UserRoleService::can('can-view-discover', $request->user()->id)) {
return [
'accounts' => [],
'hashtags' => [],
'statuses' => []
];
}
$mastodonMode = !$request->has('_pe');
return $this->json(SearchApiV2Service::query($request, $mastodonMode));
}
/**
* GET /api/v2/streaming/config
*
*
* @return object
*/
public function getWebsocketConfig()
{
return config('broadcasting.default') === 'pusher' ? [
'host' => config('broadcasting.connections.pusher.options.host'),
'port' => config('broadcasting.connections.pusher.options.port'),
'key' => config('broadcasting.connections.pusher.key'),
'cluster' => config('broadcasting.connections.pusher.options.cluster')
] : [];
}
/**
* POST /api/v2/media
*
*
* @return MediaTransformer
*/
public function mediaUploadV2(Request $request)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('write'), 403);
$this->validate($request, [
'file.*' => [
'required_without:file',
'mimetypes:' . config_cache('pixelfed.media_types'),
'max:' . config_cache('pixelfed.max_photo_size'),
],
'file' => [
'required_without:file.*',
'mimetypes:' . config_cache('pixelfed.media_types'),
'max:' . config_cache('pixelfed.max_photo_size'),
],
'filter_name' => 'nullable|string|max:24',
'filter_class' => 'nullable|alpha_dash|max:24',
'description' => 'nullable|string|max:' . config_cache('pixelfed.max_altext_length'),
'replace_id' => 'sometimes'
]);
$user = $request->user();
if($user->last_active_at == null) {
return [];
}
if(empty($request->file('file'))) {
return response('', 422);
}
$limitKey = 'compose:rate-limit:media-upload:' . $user->id;
$limitTtl = now()->addMinutes(15);
$limitReached = Cache::remember($limitKey, $limitTtl, function() use($user) {
$dailyLimit = Media::whereUserId($user->id)->where('created_at', '>', now()->subDays(1))->count();
return $dailyLimit >= 1250;
});
abort_if($limitReached == true, 429);
$profile = $user->profile;
if(config_cache('pixelfed.enforce_account_limit') == true) {
$size = Cache::remember($user->storageUsedKey(), now()->addDays(3), function() use($user) {
return Media::whereUserId($user->id)->sum('size') / 1000;
});
$limit = (int) config_cache('pixelfed.max_account_size');
if ($size >= $limit) {
abort(403, 'Account size limit reached.');
}
}
$filterClass = in_array($request->input('filter_class'), Filter::classes()) ? $request->input('filter_class') : null;
$filterName = in_array($request->input('filter_name'), Filter::names()) ? $request->input('filter_name') : null;
$photo = $request->file('file');
$mimes = explode(',', config_cache('pixelfed.media_types'));
if(in_array($photo->getMimeType(), $mimes) == false) {
abort(403, 'Invalid or unsupported mime type.');
}
$storagePath = MediaPathService::get($user, 2);
$path = $photo->storePublicly($storagePath);
$hash = \hash_file('sha256', $photo);
$license = null;
$mime = $photo->getMimeType();
$settings = UserSetting::whereUserId($user->id)->first();
if($settings && !empty($settings->compose_settings)) {
$compose = $settings->compose_settings;
if(isset($compose['default_license']) && $compose['default_license'] != 1) {
$license = $compose['default_license'];
}
}
abort_if(MediaBlocklistService::exists($hash) == true, 451);
if($request->has('replace_id')) {
$rpid = $request->input('replace_id');
$removeMedia = Media::whereNull('status_id')
->whereUserId($user->id)
->whereProfileId($profile->id)
->where('created_at', '>', now()->subHours(2))
->find($rpid);
if($removeMedia) {
MediaDeletePipeline::dispatch($removeMedia)
->onQueue('mmo')
->delay(now()->addMinutes(15));
}
}
$media = new Media();
$media->status_id = null;
$media->profile_id = $profile->id;
$media->user_id = $user->id;
$media->media_path = $path;
$media->original_sha256 = $hash;
$media->size = $photo->getSize();
$media->mime = $mime;
$media->caption = $request->input('description');
$media->filter_class = $filterClass;
$media->filter_name = $filterName;
if($license) {
$media->license = $license;
}
$media->save();
switch ($media->mime) {
case 'image/jpeg':
case 'image/png':
ImageOptimize::dispatch($media)->onQueue('mmo');
break;
case 'video/mp4':
VideoThumbnail::dispatch($media)->onQueue('mmo');
$preview_url = '/storage/no-preview.png';
$url = '/storage/no-preview.png';
break;
}
Cache::forget($limitKey);
$fractal = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer());
$resource = new Fractal\Resource\Item($media, new MediaTransformer());
$res = $fractal->createData($resource)->toArray();
$res['preview_url'] = $media->url(). '?v=' . time();
$res['url'] = null;
return $this->json($res, 202);
}
}

Wyświetl plik

@ -99,6 +99,7 @@ class BaseApiController extends Controller
public function avatarUpdate(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'upload' => 'required|mimetypes:image/jpeg,image/jpg,image/png|max:'.config('pixelfed.max_avatar_size'),
]);
@ -112,7 +113,7 @@ class BaseApiController extends Controller
$name = $path['name'];
$public = $path['storage'];
$currentAvatar = storage_path('app/'.$profile->avatar->media_path);
$loc = $request->file('upload')->storeAs($public, $name);
$loc = $request->file('upload')->storePubliclyAs($public, $name);
$avatar = Avatar::whereProfileId($profile->id)->firstOrFail();
$opath = $avatar->media_path;
@ -134,9 +135,10 @@ class BaseApiController extends Controller
public function verifyCredentials(Request $request)
{
abort_if(!$request->user(), 403);
$user = $request->user();
abort_if(!$user, 403);
if($user->status != null) {
if ($user->status != null) {
Auth::logout();
abort(403);
}
@ -147,6 +149,7 @@ class BaseApiController extends Controller
public function accountLikes(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'page' => 'sometimes|int|min:1|max:20',
'limit' => 'sometimes|int|min:1|max:10'

Wyświetl plik

@ -0,0 +1,119 @@
<?php
namespace App\Http\Controllers\Api\V1;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\UserDomainBlock;
use App\Util\ActivityPub\Helpers;
use App\Services\UserFilterService;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Cache;
use App\Jobs\HomeFeedPipeline\FeedRemoveDomainPipeline;
use App\Jobs\ProfilePipeline\ProfilePurgeNotificationsByDomain;
use App\Jobs\ProfilePipeline\ProfilePurgeFollowersByDomain;
class DomainBlockController extends Controller
{
public function json($res, $code = 200, $headers = [])
{
return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
}
public function index(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'limit' => 'sometimes|integer|min:1|max:200'
]);
$limit = $request->input('limit', 100);
$id = $request->user()->profile_id;
$filters = UserDomainBlock::whereProfileId($id)->orderByDesc('id')->cursorPaginate($limit);
$links = null;
$headers = [];
if($filters->nextCursor()) {
$links .= '<'.$filters->nextPageUrl().'&limit='.$limit.'>; rel="next"';
}
if($filters->previousCursor()) {
if($links != null) {
$links .= ', ';
}
$links .= '<'.$filters->previousPageUrl().'&limit='.$limit.'>; rel="prev"';
}
if($links) {
$headers = ['Link' => $links];
}
return $this->json($filters->pluck('domain'), 200, $headers);
}
public function store(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'domain' => 'required|active_url|min:1|max:120'
]);
$pid = $request->user()->profile_id;
$domain = trim($request->input('domain'));
if(Helpers::validateUrl($domain) == false) {
return abort(500, 'Invalid domain or already blocked by server admins');
}
$domain = strtolower(parse_url($domain, PHP_URL_HOST));
abort_if(config_cache('pixelfed.domain.app') == $domain, 400, 'Cannot ban your own server');
$existingCount = UserDomainBlock::whereProfileId($pid)->count();
$maxLimit = (int) config_cache('instance.user_filters.max_domain_blocks');
$errorMsg = __('profile.block.domain.max', ['max' => $maxLimit]);
abort_if($existingCount >= $maxLimit, 400, $errorMsg);
$block = UserDomainBlock::updateOrCreate([
'profile_id' => $pid,
'domain' => $domain
]);
if($block->wasRecentlyCreated) {
Bus::batch([
[
new FeedRemoveDomainPipeline($pid, $domain),
new ProfilePurgeNotificationsByDomain($pid, $domain),
new ProfilePurgeFollowersByDomain($pid, $domain)
]
])->allowFailures()->onQueue('feed')->dispatch();
Cache::forget('profile:following:' . $pid);
UserFilterService::domainBlocks($pid, true);
}
return $this->json([]);
}
public function delete(Request $request)
{
abort_if(!$request->user(), 403);
$this->validate($request, [
'domain' => 'required|min:1|max:120'
]);
$pid = $request->user()->profile_id;
$domain = strtolower(trim($request->input('domain')));
$filters = UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->delete();
UserFilterService::domainBlocks($pid, true);
return $this->json([]);
}
}

Wyświetl plik

@ -0,0 +1,209 @@
<?php
namespace App\Http\Controllers\Api\V1;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Hashtag;
use App\HashtagFollow;
use App\StatusHashtag;
use App\Services\AccountService;
use App\Services\HashtagService;
use App\Services\HashtagFollowService;
use App\Services\HashtagRelatedService;
use App\Http\Resources\MastoApi\FollowedTagResource;
use App\Jobs\HomeFeedPipeline\FeedWarmCachePipeline;
use App\Jobs\HomeFeedPipeline\HashtagUnfollowPipeline;
class TagsController extends Controller
{
const PF_API_ENTITY_KEY = "_pe";
public function json($res, $code = 200, $headers = [])
{
return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
}
/**
* GET /api/v1/tags/:id/related
*
*
* @return array
*/
public function relatedTags(Request $request, $tag)
{
abort_if(!$request->user() || !$request->user()->token(), 403);
abort_unless($request->user()->tokenCan('read'), 403);
$tag = Hashtag::whereSlug($tag)->firstOrFail();
return HashtagRelatedService::get($tag->id);
}
/**
* POST /api/v1/tags/:id/follow
*
*
* @return object
*/
public function followHashtag(Request $request, $id)
{
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$account = AccountService::get($pid);
$operator = config('database.default') == 'pgsql' ? 'ilike' : 'like';
$tag = Hashtag::where('name', $operator, $id)
->orWhere('slug', $operator, $id)
->first();
abort_if(!$tag, 422, 'Unknown hashtag');
abort_if(
HashtagFollow::whereProfileId($pid)->count() >= HashtagFollow::MAX_LIMIT,
422,
'You cannot follow more than ' . HashtagFollow::MAX_LIMIT . ' hashtags.'
);
$follows = HashtagFollow::updateOrCreate(
[
'profile_id' => $account['id'],
'hashtag_id' => $tag->id
],
[
'user_id' => $request->user()->id
]
);
HashtagService::follow($pid, $tag->id);
HashtagFollowService::add($tag->id, $pid);
return response()->json(FollowedTagResource::make($follows)->toArray($request));
}
/**
* POST /api/v1/tags/:id/unfollow
*
*
* @return object
*/
public function unfollowHashtag(Request $request, $id)
{
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$account = AccountService::get($pid);
$operator = config('database.default') == 'pgsql' ? 'ilike' : 'like';
$tag = Hashtag::where('name', $operator, $id)
->orWhere('slug', $operator, $id)
->first();
abort_if(!$tag, 422, 'Unknown hashtag');
$follows = HashtagFollow::whereProfileId($pid)
->whereHashtagId($tag->id)
->first();
if(!$follows) {
return [
'name' => $tag->name,
'url' => config('app.url') . '/i/web/hashtag/' . $tag->slug,
'history' => [],
'following' => false
];
}
if($follows) {
HashtagService::unfollow($pid, $tag->id);
HashtagFollowService::unfollow($tag->id, $pid);
HashtagUnfollowPipeline::dispatch($tag->id, $pid, $tag->slug)->onQueue('feed');
$follows->delete();
}
$res = FollowedTagResource::make($follows)->toArray($request);
$res['following'] = false;
return response()->json($res);
}
/**
* GET /api/v1/tags/:id
*
*
* @return object
*/
public function getHashtag(Request $request, $id)
{
abort_if(!$request->user(), 403);
$pid = $request->user()->profile_id;
$account = AccountService::get($pid);
$operator = config('database.default') == 'pgsql' ? 'ilike' : 'like';
$tag = Hashtag::where('name', $operator, $id)
->orWhere('slug', $operator, $id)
->first();
if(!$tag) {
return [
'name' => $id,
'url' => config('app.url') . '/i/web/hashtag/' . $id,
'history' => [],
'following' => false
];
}
$res = [
'name' => $tag->name,
'url' => config('app.url') . '/i/web/hashtag/' . $tag->slug,
'history' => [],
'following' => HashtagService::isFollowing($pid, $tag->id)
];
if($request->has(self::PF_API_ENTITY_KEY)) {
$res['count'] = HashtagService::count($tag->id);
}
return $this->json($res);
}
/**
* GET /api/v1/followed_tags
*
*
* @return array
*/
public function getFollowedTags(Request $request)
{
abort_if(!$request->user(), 403);
$account = AccountService::get($request->user()->profile_id);
$this->validate($request, [
'cursor' => 'sometimes',
'limit' => 'sometimes|integer|min:1|max:200'
]);
$limit = $request->input('limit', 100);
$res = HashtagFollow::whereProfileId($account['id'])
->orderByDesc('id')
->cursorPaginate($limit)
->withQueryString();
$pagination = false;
$prevPage = $res->nextPageUrl();
$nextPage = $res->previousPageUrl();
if($nextPage && $prevPage) {
$pagination = '<' . $nextPage . '>; rel="next", <' . $prevPage . '>; rel="prev"';
} else if($nextPage && !$prevPage) {
$pagination = '<' . $nextPage . '>; rel="next"';
} else if(!$nextPage && $prevPage) {
$pagination = '<' . $prevPage . '>; rel="prev"';
}
if($pagination) {
return response()->json(FollowedTagResource::collection($res)->collection)
->header('Link', $pagination);
}
return response()->json(FollowedTagResource::collection($res)->collection);
}
}

Wyświetl plik

@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use App\Services\BouncerService;
use Illuminate\Http\Request;
class ForgotPasswordController extends Controller
{
@ -29,4 +31,74 @@ class ForgotPasswordController extends Controller
{
$this->middleware('guest');
}
/**
* Display the form to request a password reset link.
*
* @return \Illuminate\View\View
*/
public function showLinkRequestForm()
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp(request()->ip()), 404);
}
usleep(random_int(100000, 300000));
return view('auth.passwords.email');
}
/**
* Validate the email for the given request.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function validateEmail(Request $request)
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
usleep(random_int(100000, 3000000));
if((bool) config_cache('captcha.enabled')) {
$rules = [
'email' => 'required|email',
'h-captcha-response' => 'required|captcha'
];
} else {
$rules = [
'email' => 'required|email'
];
}
$request->validate($rules, [
'h-captcha-response' => 'Failed to validate the captcha.',
]);
}
/**
* Get the response for a failed password reset link.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function sendResetLinkFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages([
'email' => [trans($response)],
]);
}
return back()
->withInput($request->only('email'))
->withErrors([
'email' => trans($response),
]);
}
}

Wyświetl plik

@ -6,6 +6,9 @@ use App\AccountLog;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Services\BouncerService;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
@ -42,6 +45,15 @@ class LoginController extends Controller
$this->middleware('guest')->except('logout');
}
public function showLoginForm()
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp(request()->ip()), 404);
}
return view('auth.login');
}
/**
* Validate the user login request.
*
@ -51,16 +63,29 @@ class LoginController extends Controller
*/
public function validateLogin($request)
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$rules = [
$this->username() => 'required|email',
'password' => 'required|string|min:6',
];
$messages = [];
if(config('captcha.enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
if(
(bool) config_cache('captcha.enabled') &&
(bool) config_cache('captcha.active.login') ||
(
(bool) config_cache('captcha.triggers.login.enabled') &&
request()->session()->has('login_attempts') &&
request()->session()->get('login_attempts') >= config('captcha.triggers.login.attempts')
)
) {
$rules['h-captcha-response'] = 'required|filled|captcha|min:5';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules);
$request->validate($rules, $messages);
}
/**
@ -88,4 +113,28 @@ class LoginController extends Controller
$log->user_agent = $request->userAgent();
$log->save();
}
/**
* Get the failed login response instance.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function sendFailedLoginResponse(Request $request)
{
if(config('captcha.triggers.login.enabled')) {
if ($request->session()->has('login_attempts')) {
$ct = $request->session()->get('login_attempts');
$request->session()->put('login_attempts', $ct + 1);
} else {
$request->session()->put('login_attempts', 1);
}
}
throw ValidationException::withMessages([
$this->username() => [trans('auth.failed')],
]);
}
}

Wyświetl plik

@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Validator;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use App\Services\EmailService;
use App\Services\BouncerService;
class RegisterController extends Controller
{
@ -59,7 +60,7 @@ class RegisterController extends Controller
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
public function validator(array $data)
{
if(config('database.default') == 'pgsql') {
$data['username'] = strtolower($data['username']);
@ -136,7 +137,7 @@ class RegisterController extends Controller
'password' => 'required|string|min:'.config('pixelfed.min_password_length').'|confirmed',
];
if(config('captcha.enabled')) {
if((bool) config_cache('captcha.enabled') && (bool) config_cache('captcha.active.register')) {
$rules['h-captcha-response'] = 'required|captcha';
}
@ -150,7 +151,7 @@ class RegisterController extends Controller
*
* @return \App\User
*/
protected function create(array $data)
public function create(array $data)
{
if(config('database.default') == 'pgsql') {
$data['username'] = strtolower($data['username']);
@ -162,6 +163,7 @@ class RegisterController extends Controller
'username' => $data['username'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'app_register_ip' => request()->ip()
]);
}
@ -172,16 +174,28 @@ class RegisterController extends Controller
*/
public function showRegistrationForm()
{
if(config_cache('pixelfed.open_registration')) {
$limit = config('pixelfed.max_users');
if($limit) {
abort_if($limit <= User::count(), 404);
if((bool) config_cache('pixelfed.open_registration')) {
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp(request()->ip()), 404);
}
$hasLimit = config('pixelfed.enforce_max_users');
if($hasLimit) {
$limit = config('pixelfed.max_users');
$count = User::where(function($q){ return $q->whereNull('status')->orWhereNotIn('status', ['deleted','delete']); })->count();
if($limit <= $count) {
return redirect(route('help.instance-max-users-limit'));
}
abort_if($limit <= $count, 404);
return view('auth.register');
} else {
return view('auth.register');
}
} else {
abort(404);
if((bool) config_cache('instance.curated_registration.enabled') && config('instance.curated_registration.state.fallback_on_closed_reg')) {
return redirect('/auth/sign_up');
} else {
abort(404);
}
}
}
@ -195,13 +209,21 @@ class RegisterController extends Controller
{
abort_if(config_cache('pixelfed.open_registration') == false, 400);
$count = User::count();
$limit = config('pixelfed.max_users');
if(false == config_cache('pixelfed.open_registration') || $limit && $limit <= $count) {
return abort(403);
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$hasLimit = config('pixelfed.enforce_max_users');
if($hasLimit) {
$count = User::where(function($q){ return $q->whereNull('status')->orWhereNotIn('status', ['deleted','delete']); })->count();
$limit = config('pixelfed.max_users');
if($limit && $limit <= $count) {
return redirect(route('help.instance-max-users-limit'));
}
}
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all())));

Wyświetl plik

@ -4,6 +4,10 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Password;
use Illuminate\Http\Request;
use App\Services\BouncerService;
use Illuminate\Validation\Rules;
class ResetPasswordController extends Controller
{
@ -36,4 +40,123 @@ class ResetPasswordController extends Controller
{
$this->middleware('guest');
}
/**
* Get the password reset validation rules.
*
* @return array
*/
protected function rules()
{
usleep(random_int(100000, 3000000));
if((bool) config_cache('captcha.enabled')) {
return [
'token' => 'required',
'email' => 'required|email',
'password' => ['required', 'confirmed', 'max:72', Rules\Password::defaults()],
'h-captcha-response' => ['required' ,'filled', 'captcha']
];
}
return [
'token' => 'required',
'email' => 'required|email',
'password' => ['required', 'confirmed', 'max:72', Rules\Password::defaults()],
];
}
/**
* Get the password reset validation error messages.
*
* @return array
*/
protected function validationErrorMessages()
{
return [
'password.max' => 'Passwords should not exceed 72 characters.',
'h-captcha-response.required' => 'Failed to validate the captcha.',
'h-captcha-response.filled' => 'Failed to validate the captcha.',
'h-captcha-response.captcha' => 'Failed to validate the captcha.',
];
}
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function showResetForm(Request $request)
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
usleep(random_int(100000, 300000));
$token = $request->route()->parameter('token');
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
public function reset(Request $request)
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$request->validate($this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
/**
* Get the password reset credentials from the request.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function credentials(Request $request)
{
return $request->only(
'email', 'password', 'password_confirmation', 'token'
);
}
/**
* Get the response for a failed password reset.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages(['email' => [trans($response)]]);
}
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => [trans($response)]]);
}
}

Wyświetl plik

@ -30,7 +30,7 @@ class AvatarController extends Controller
$dir = $path['root'];
$name = $path['name'];
$public = $path['storage'];
$loc = $request->file('avatar')->storeAs($public, $name);
$loc = $request->file('avatar')->storePubliclyAs($public, $name);
$avatar = Avatar::firstOrNew(['profile_id' => $profile->id]);
$currentAvatar = $avatar->recentlyCreated ? null : storage_path('app/'.$profile->avatar->media_path);

Wyświetl plik

@ -8,60 +8,56 @@ use Auth;
use Illuminate\Http\Request;
use App\Services\BookmarkService;
use App\Services\FollowerService;
use App\Services\UserRoleService;
class BookmarkController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request)
{
$this->validate($request, [
'item' => 'required|integer|min:1',
]);
public function store(Request $request)
{
$this->validate($request, [
'item' => 'required|integer|min:1',
]);
$profile = Auth::user()->profile;
$status = Status::findOrFail($request->input('item'));
$user = $request->user();
$status = Status::findOrFail($request->input('item'));
abort_if($status->in_reply_to_id || $status->reblog_of_id, 404);
abort_if(!in_array($status->scope, ['public', 'unlisted', 'private']), 404);
abort_if(!in_array($status->type, ['photo','photo:album', 'video', 'video:album', 'photo:video:album']), 404);
abort_if($user->has_roles && !UserRoleService::can('can-bookmark', $user->id), 403, 'Invalid permissions for this action');
abort_if($status->in_reply_to_id || $status->reblog_of_id, 404);
abort_if(!in_array($status->scope, ['public', 'unlisted', 'private']), 404);
abort_if(!in_array($status->type, ['photo','photo:album', 'video', 'video:album', 'photo:video:album']), 404);
if($status->scope == 'private') {
if($profile->id !== $status->profile_id && !FollowerService::follows($profile->id, $status->profile_id)) {
if($exists = Bookmark::whereStatusId($status->id)->whereProfileId($profile->id)->first()) {
BookmarkService::del($profile->id, $status->id);
$exists->delete();
if($status->scope == 'private') {
if($user->profile_id !== $status->profile_id && !FollowerService::follows($user->profile_id, $status->profile_id)) {
if($exists = Bookmark::whereStatusId($status->id)->whereProfileId($user->profile_id)->first()) {
BookmarkService::del($user->profile_id, $status->id);
$exists->delete();
if ($request->ajax()) {
return ['code' => 200, 'msg' => 'Bookmark removed!'];
} else {
return redirect()->back();
}
}
abort(404, 'Error: You cannot bookmark private posts from accounts you do not follow.');
}
}
if ($request->ajax()) {
return ['code' => 200, 'msg' => 'Bookmark removed!'];
} else {
return redirect()->back();
}
}
abort(404, 'Error: You cannot bookmark private posts from accounts you do not follow.');
}
}
$bookmark = Bookmark::firstOrCreate(
['status_id' => $status->id], ['profile_id' => $profile->id]
);
$bookmark = Bookmark::firstOrCreate(
['status_id' => $status->id], ['profile_id' => $user->profile_id]
);
if (!$bookmark->wasRecentlyCreated) {
BookmarkService::del($profile->id, $status->id);
$bookmark->delete();
} else {
BookmarkService::add($profile->id, $status->id);
}
if (!$bookmark->wasRecentlyCreated) {
BookmarkService::del($user->profile_id, $status->id);
$bookmark->delete();
} else {
BookmarkService::add($user->profile_id, $status->id);
}
if ($request->ajax()) {
$response = ['code' => 200, 'msg' => 'Bookmark saved!'];
} else {
$response = redirect()->back();
}
return $response;
}
return $request->expectsJson() ? ['code' => 200, 'msg' => 'Bookmark saved!'] : redirect()->back();
}
}

Wyświetl plik

@ -153,7 +153,7 @@ class CollectionController extends Controller
abort(400, 'You can only add '.$max.' posts per collection');
}
$status = Status::whereScope('public')
$status = Status::whereIn('scope', ['public', 'unlisted'])
->whereProfileId($profileId)
->whereIn('type', ['photo', 'photo:album', 'video'])
->findOrFail($postId);
@ -166,17 +166,13 @@ class CollectionController extends Controller
'order' => $count,
]);
CollectionService::addItem(
$collection->id,
$status->id,
$count
);
CollectionService::deleteCollection($collection->id);
$collection->updated_at = now();
$collection->save();
CollectionService::setCollection($collection->id, $collection);
return StatusService::get($status->id);
return StatusService::get($status->id, false);
}
public function getCollection(Request $request, $id)
@ -226,10 +222,10 @@ class CollectionController extends Controller
return collect($items)
->map(function($id) {
return StatusService::get($id);
return StatusService::get($id, false);
})
->filter(function($item) {
return $item && isset($item['account'], $item['media_attachments']);
return $item && ($item['visibility'] == 'public' || $item['visibility'] == 'unlisted') && isset($item['account'], $item['media_attachments']);
})
->values();
}
@ -298,7 +294,7 @@ class CollectionController extends Controller
abort(400, 'You cannot delete the only post of a collection!');
}
$status = Status::whereScope('public')
$status = Status::whereIn('scope', ['public', 'unlisted'])
->whereIn('type', ['photo', 'photo:album', 'video'])
->findOrFail($postId);

Wyświetl plik

@ -2,23 +2,18 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use DB;
use Cache;
use App\Comment;
use App\Jobs\CommentPipeline\CommentPipeline;
use App\Jobs\StatusPipeline\NewStatusPipeline;
use App\Util\Lexer\Autolink;
use App\Profile;
use App\Status;
use App\UserFilter;
use League\Fractal;
use App\Transformer\Api\StatusTransformer;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\StatusService;
use App\Status;
use App\Transformer\Api\StatusTransformer;
use App\UserFilter;
use App\Util\Lexer\Autolink;
use Auth;
use DB;
use Illuminate\Http\Request;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
class CommentController extends Controller
{
@ -33,9 +28,9 @@ class CommentController extends Controller
abort(403);
}
$this->validate($request, [
'item' => 'required|integer|min:1',
'comment' => 'required|string|max:'.(int) config('pixelfed.max_caption_length'),
'sensitive' => 'nullable|boolean'
'item' => 'required|integer|min:1',
'comment' => 'required|string|max:'.config_cache('pixelfed.max_caption_length'),
'sensitive' => 'nullable|boolean',
]);
$comment = $request->input('comment');
$statusId = $request->input('item');
@ -45,7 +40,7 @@ class CommentController extends Controller
$profile = $user->profile;
$status = Status::findOrFail($statusId);
if($status->comments_disabled == true) {
if ($status->comments_disabled == true) {
return;
}
@ -55,11 +50,11 @@ class CommentController extends Controller
->whereFilterableId($profile->id)
->exists();
if($filtered == true) {
if ($filtered == true) {
return;
}
$reply = DB::transaction(function() use($comment, $status, $profile, $nsfw) {
$reply = DB::transaction(function () use ($comment, $status, $profile, $nsfw) {
$scope = $profile->is_private == true ? 'private' : 'public';
$autolink = Autolink::create()->autolink($comment);
$reply = new Status();

Wyświetl plik

@ -46,7 +46,7 @@ class ContactController extends Controller
$contact->response = '';
$contact->save();
ContactPipeline::dispatchNow($contact);
ContactPipeline::dispatch($contact);
return redirect()->back()->with('status', 'Success - Your message has been sent to admins.');
}

Wyświetl plik

@ -0,0 +1,399 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\User;
use App\Models\CuratedRegister;
use App\Models\CuratedRegisterActivity;
use App\Services\EmailService;
use App\Services\BouncerService;
use App\Util\Lexer\RestrictedNames;
use App\Mail\CuratedRegisterConfirmEmail;
use App\Mail\CuratedRegisterNotifyAdmin;
use Illuminate\Support\Facades\Mail;
use App\Jobs\CuratedOnboarding\CuratedOnboardingNotifyAdminNewApplicationPipeline;
class CuratedRegisterController extends Controller
{
public function __construct()
{
abort_unless((bool) config_cache('instance.curated_registration.enabled'), 404);
if((bool) config_cache('pixelfed.open_registration')) {
abort_if(config('instance.curated_registration.state.only_enabled_on_closed_reg'), 404);
} else {
abort_unless(config('instance.curated_registration.state.fallback_on_closed_reg'), 404);
}
}
public function index(Request $request)
{
abort_if($request->user(), 404);
return view('auth.curated-register.index', ['step' => 1]);
}
public function concierge(Request $request)
{
abort_if($request->user(), 404);
$emailConfirmed = $request->session()->has('cur-reg-con.email-confirmed') &&
$request->has('next') &&
$request->session()->has('cur-reg-con.cr-id');
return view('auth.curated-register.concierge', compact('emailConfirmed'));
}
public function conciergeResponseSent(Request $request)
{
return view('auth.curated-register.user_response_sent');
}
public function conciergeFormShow(Request $request)
{
abort_if($request->user(), 404);
abort_unless(
$request->session()->has('cur-reg-con.email-confirmed') &&
$request->session()->has('cur-reg-con.cr-id') &&
$request->session()->has('cur-reg-con.ac-id'), 404);
$crid = $request->session()->get('cur-reg-con.cr-id');
$arid = $request->session()->get('cur-reg-con.ac-id');
$showCaptcha = config('instance.curated_registration.captcha_enabled');
if($attempts = $request->session()->get('cur-reg-con-attempt')) {
$showCaptcha = $attempts && $attempts >= 2;
} else {
$showCaptcha = false;
}
$activity = CuratedRegisterActivity::whereRegisterId($crid)->whereFromAdmin(true)->findOrFail($arid);
return view('auth.curated-register.concierge_form', compact('activity', 'showCaptcha'));
}
public function conciergeFormStore(Request $request)
{
abort_if($request->user(), 404);
$request->session()->increment('cur-reg-con-attempt');
abort_unless(
$request->session()->has('cur-reg-con.email-confirmed') &&
$request->session()->has('cur-reg-con.cr-id') &&
$request->session()->has('cur-reg-con.ac-id'), 404);
$attempts = $request->session()->get('cur-reg-con-attempt');
$messages = [];
$rules = [
'response' => 'required|string|min:5|max:1000',
'crid' => 'required|integer|min:1',
'acid' => 'required|integer|min:1'
];
if(config('instance.curated_registration.captcha_enabled') && $attempts >= 3) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$crid = $request->session()->get('cur-reg-con.cr-id');
$acid = $request->session()->get('cur-reg-con.ac-id');
abort_if((string) $crid !== $request->input('crid'), 404);
abort_if((string) $acid !== $request->input('acid'), 404);
if(CuratedRegisterActivity::whereRegisterId($crid)->whereReplyToId($acid)->exists()) {
return redirect()->back()->withErrors(['code' => 'You already replied to this request.']);
}
$act = CuratedRegisterActivity::create([
'register_id' => $crid,
'reply_to_id' => $acid,
'type' => 'user_response',
'message' => $request->input('response'),
'from_user' => true,
'action_required' => true,
]);
CuratedRegister::findOrFail($crid)->update(['user_has_responded' => true]);
$request->session()->pull('cur-reg-con');
$request->session()->pull('cur-reg-con-attempt');
return view('auth.curated-register.user_response_sent');
}
public function conciergeStore(Request $request)
{
abort_if($request->user(), 404);
$rules = [
'sid' => 'required_if:action,email|integer|min:1|max:20000000',
'id' => 'required_if:action,email|integer|min:1|max:20000000',
'code' => 'required_if:action,email',
'action' => 'required|string|in:email,message',
'email' => 'required_if:action,email|email',
'response' => 'required_if:action,message|string|min:20|max:1000',
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$action = $request->input('action');
$sid = $request->input('sid');
$id = $request->input('id');
$code = $request->input('code');
$email = $request->input('email');
$cr = CuratedRegister::whereIsClosed(false)->findOrFail($sid);
$ac = CuratedRegisterActivity::whereRegisterId($cr->id)->whereFromAdmin(true)->findOrFail($id);
if(!hash_equals($ac->secret_code, $code)) {
return redirect()->back()->withErrors(['code' => 'Invalid code']);
}
if(!hash_equals($cr->email, $email)) {
return redirect()->back()->withErrors(['email' => 'Invalid email']);
}
$request->session()->put('cur-reg-con.email-confirmed', true);
$request->session()->put('cur-reg-con.cr-id', $cr->id);
$request->session()->put('cur-reg-con.ac-id', $ac->id);
$emailConfirmed = true;
return redirect('/auth/sign_up/concierge/form');
}
public function confirmEmail(Request $request)
{
if($request->user()) {
return redirect(route('help.email-confirmation-issues'));
}
return view('auth.curated-register.confirm_email');
}
public function emailConfirmed(Request $request)
{
if($request->user()) {
return redirect(route('help.email-confirmation-issues'));
}
return view('auth.curated-register.email_confirmed');
}
public function resendConfirmation(Request $request)
{
return view('auth.curated-register.resend-confirmation');
}
public function resendConfirmationProcess(Request $request)
{
$rules = [
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'exists:curated_registers',
]
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$cur = CuratedRegister::whereEmail($request->input('email'))->whereIsClosed(false)->first();
if(!$cur) {
return redirect()->back()->withErrors(['email' => 'The selected email is invalid.']);
}
$totalCount = CuratedRegisterActivity::whereRegisterId($cur->id)
->whereType('user_resend_email_confirmation')
->count();
if($totalCount && $totalCount >= config('instance.curated_registration.resend_confirmation_limit')) {
return redirect()->back()->withErrors(['email' => 'You have re-attempted too many times. To proceed with your application, please <a href="/site/contact" class="text-white" style="text-decoration: underline;">contact the admin team</a>.']);
}
$count = CuratedRegisterActivity::whereRegisterId($cur->id)
->whereType('user_resend_email_confirmation')
->where('created_at', '>', now()->subHours(12))
->count();
if($count) {
return redirect()->back()->withErrors(['email' => 'You can only re-send the confirmation email once per 12 hours. Try again later.']);
}
CuratedRegisterActivity::create([
'register_id' => $cur->id,
'type' => 'user_resend_email_confirmation',
'admin_only_view' => true,
'from_admin' => false,
'from_user' => false,
'action_required' => false,
]);
Mail::to($cur->email)->send(new CuratedRegisterConfirmEmail($cur));
return view('auth.curated-register.resent-confirmation');
return $request->all();
}
public function confirmEmailHandle(Request $request)
{
$rules = [
'sid' => 'required',
'code' => 'required'
];
$messages = [];
if(config('instance.curated_registration.captcha_enabled')) {
$rules['h-captcha-response'] = 'required|captcha';
$messages['h-captcha-response.required'] = 'The captcha must be filled';
}
$this->validate($request, $rules, $messages);
$cr = CuratedRegister::whereNull('email_verified_at')
->where('created_at', '>', now()->subHours(24))
->find($request->input('sid'));
if(!$cr) {
return redirect(route('help.email-confirmation-issues'));
}
if(!hash_equals($cr->verify_code, $request->input('code'))) {
return redirect(route('help.email-confirmation-issues'));
}
$cr->email_verified_at = now();
$cr->save();
if(config('instance.curated_registration.notify.admin.on_verify_email.enabled')) {
CuratedOnboardingNotifyAdminNewApplicationPipeline::dispatch($cr);
}
return view('auth.curated-register.email_confirmed');
}
public function proceed(Request $request)
{
$this->validate($request, [
'step' => 'required|integer|in:1,2,3,4'
]);
$step = $request->input('step');
switch($step) {
case 1:
$step = 2;
$request->session()->put('cur-step', 1);
return view('auth.curated-register.index', compact('step'));
break;
case 2:
$this->stepTwo($request);
$step = 3;
$request->session()->put('cur-step', 2);
return view('auth.curated-register.index', compact('step'));
break;
case 3:
$this->stepThree($request);
$step = 3;
$request->session()->put('cur-step', 3);
$verifiedEmail = true;
$request->session()->pull('cur-reg');
return view('auth.curated-register.index', compact('step', 'verifiedEmail'));
break;
}
}
protected function stepTwo($request)
{
if($request->filled('reason')) {
$request->session()->put('cur-reg.form-reason', $request->input('reason'));
}
if($request->filled('username')) {
$request->session()->put('cur-reg.form-username', $request->input('username'));
}
if($request->filled('email')) {
$request->session()->put('cur-reg.form-email', $request->input('email'));
}
$this->validate($request, [
'username' => [
'required',
'min:2',
'max:15',
'unique:curated_registers',
'unique:users',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
$underscore = substr_count($value, '_');
$period = substr_count($value, '.');
if(ends_with($value, ['.php', '.js', '.css'])) {
return $fail('Username is invalid.');
}
if(($dash + $underscore + $period) > 1) {
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
}
if (!ctype_alnum($value[0])) {
return $fail('Username is invalid. Must start with a letter or number.');
}
if (!ctype_alnum($value[strlen($value) - 1])) {
return $fail('Username is invalid. Must end with a letter or number.');
}
$val = str_replace(['_', '.', '-'], '', $value);
if(!ctype_alnum($val)) {
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
}
$restricted = RestrictedNames::get();
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
return $fail('Username cannot be used.');
}
},
],
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'max:255',
'unique:users',
'unique:curated_registers',
function ($attribute, $value, $fail) {
$banned = EmailService::isBanned($value);
if($banned) {
return $fail('Email is invalid.');
}
},
],
'password' => 'required|min:8',
'password_confirmation' => 'required|same:password',
'reason' => 'required|min:20|max:1000',
'agree' => 'required|accepted'
]);
$request->session()->put('cur-reg.form-email', $request->input('email'));
$request->session()->put('cur-reg.form-password', $request->input('password'));
}
protected function stepThree($request)
{
$this->validate($request, [
'email' => [
'required',
'string',
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
'max:255',
'unique:users',
'unique:curated_registers',
function ($attribute, $value, $fail) {
$banned = EmailService::isBanned($value);
if($banned) {
return $fail('Email is invalid.');
}
},
]
]);
$cr = new CuratedRegister;
$cr->email = $request->email;
$cr->username = $request->session()->get('cur-reg.form-username');
$cr->password = bcrypt($request->session()->get('cur-reg.form-password'));
$cr->ip_address = $request->ip();
$cr->reason_to_join = $request->session()->get('cur-reg.form-reason');
$cr->verify_code = Str::random(40);
$cr->save();
Mail::to($cr->email)->send(new CuratedRegisterConfirmEmail($cr));
}
}

Wyświetl plik

@ -2,361 +2,422 @@
namespace App\Http\Controllers;
use App\{
DiscoverCategory,
Follower,
Hashtag,
HashtagFollow,
Instance,
Like,
Profile,
Status,
StatusHashtag,
UserFilter
};
use Auth, DB, Cache;
use Illuminate\Http\Request;
use App\Hashtag;
use App\Instance;
use App\Like;
use App\Services\AccountService;
use App\Services\AdminShadowFilterService;
use App\Services\BookmarkService;
use App\Services\ConfigCacheService;
use App\Services\FollowerService;
use App\Services\HashtagService;
use App\Services\LikeService;
use App\Services\ReblogService;
use App\Services\StatusHashtagService;
use App\Services\SnowflakeService;
use App\Services\StatusHashtagService;
use App\Services\StatusService;
use App\Services\TrendingHashtagService;
use App\Services\UserFilterService;
use App\Status;
use Auth;
use Cache;
use DB;
use Illuminate\Http\Request;
class DiscoverController extends Controller
{
public function home(Request $request)
{
abort_if(!Auth::check() && config('instance.discover.public') == false, 403);
return view('discover.home');
}
public function home(Request $request)
{
abort_if(! Auth::check() && config('instance.discover.public') == false, 403);
public function showTags(Request $request, $hashtag)
{
abort_if(!config('instance.discover.tags.is_public') && !Auth::check(), 403);
return view('discover.home');
}
$tag = Hashtag::whereName($hashtag)
->orWhere('slug', $hashtag)
->where('is_banned', '!=', true)
->firstOrFail();
$tagCount = StatusHashtagService::count($tag->id);
return view('discover.tags.show', compact('tag', 'tagCount'));
}
public function showTags(Request $request, $hashtag)
{
if ($request->user()) {
return redirect('/i/web/hashtag/'.$hashtag.'?src=pd');
}
abort_if(! config('instance.discover.tags.is_public') && ! Auth::check(), 403);
public function getHashtags(Request $request)
{
$user = $request->user();
abort_if(!config('instance.discover.tags.is_public') && !$user, 403);
$tag = Hashtag::whereName($hashtag)
->orWhere('slug', $hashtag)
->where('is_banned', '!=', true)
->firstOrFail();
$tagCount = $tag->cached_count ?? 0;
$this->validate($request, [
'hashtag' => 'required|string|min:1|max:124',
'page' => 'nullable|integer|min:1|max:' . ($user ? 29 : 3)
]);
return view('discover.tags.show', compact('tag', 'tagCount'));
}
$page = $request->input('page') ?? '1';
$end = $page > 1 ? $page * 9 : 0;
$tag = $request->input('hashtag');
public function getHashtags(Request $request)
{
$user = $request->user();
abort_if(! config('instance.discover.tags.is_public') && ! $user, 403);
$hashtag = Hashtag::whereName($tag)->firstOrFail();
if($hashtag->is_banned == true) {
return [];
}
if($user) {
$res['follows'] = HashtagService::isFollowing($user->profile_id, $hashtag->id);
}
$res['hashtag'] = [
'name' => $hashtag->name,
'url' => $hashtag->url()
];
if($user) {
$tags = StatusHashtagService::get($hashtag->id, $page, $end);
$res['tags'] = collect($tags)
->map(function($tag) use($user) {
$tag['status']['favourited'] = (bool) LikeService::liked($user->profile_id, $tag['status']['id']);
$tag['status']['reblogged'] = (bool) ReblogService::get($user->profile_id, $tag['status']['id']);
$tag['status']['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $tag['status']['id']);
return $tag;
})
->filter(function($tag) {
if(!StatusService::get($tag['status']['id'])) {
return false;
}
return true;
})
->values();
} else {
if($page != 1) {
$res['tags'] = [];
return $res;
}
$key = 'discover:tags:public_feed:' . $hashtag->id . ':page:' . $page;
$tags = Cache::remember($key, 43200, function() use($hashtag, $page, $end) {
return collect(StatusHashtagService::get($hashtag->id, $page, $end))
->filter(function($tag) {
if(!$tag['status']['local']) {
return false;
}
return true;
})
->values();
});
$res['tags'] = collect($tags)
->filter(function($tag) {
if(!StatusService::get($tag['status']['id'])) {
return false;
}
return true;
})
->values();
}
return $res;
}
$this->validate($request, [
'hashtag' => 'required|string|min:1|max:124',
'page' => 'nullable|integer|min:1|max:'.($user ? 29 : 3),
]);
public function profilesDirectory(Request $request)
{
return redirect('/')->with('statusRedirect', 'The Profile Directory is unavailable at this time.');
}
$page = $request->input('page') ?? '1';
$end = $page > 1 ? $page * 9 : 0;
$tag = $request->input('hashtag');
public function profilesDirectoryApi(Request $request)
{
return ['error' => 'Temporarily unavailable.'];
}
if (config('database.default') === 'pgsql') {
$hashtag = Hashtag::where('name', 'ilike', $tag)->firstOrFail();
} else {
$hashtag = Hashtag::whereName($tag)->firstOrFail();
}
public function trendingApi(Request $request)
{
abort_if(config('instance.discover.public') == false && !$request->user(), 403);
if ($hashtag->is_banned == true) {
return [];
}
if ($user) {
$res['follows'] = HashtagService::isFollowing($user->profile_id, $hashtag->id);
}
$res['hashtag'] = [
'name' => $hashtag->name,
'url' => $hashtag->url(),
];
if ($user) {
$tags = StatusHashtagService::get($hashtag->id, $page, $end);
$res['tags'] = collect($tags)
->map(function ($tag) use ($user) {
$tag['status']['favourited'] = (bool) LikeService::liked($user->profile_id, $tag['status']['id']);
$tag['status']['reblogged'] = (bool) ReblogService::get($user->profile_id, $tag['status']['id']);
$tag['status']['bookmarked'] = (bool) BookmarkService::get($user->profile_id, $tag['status']['id']);
$this->validate($request, [
'range' => 'nullable|string|in:daily,monthly,yearly',
]);
return $tag;
})
->filter(function ($tag) {
if (! StatusService::get($tag['status']['id'])) {
return false;
}
$range = $request->input('range');
$days = $range == 'monthly' ? 31 : ($range == 'daily' ? 1 : 365);
$ttls = [
1 => 1500,
31 => 14400,
365 => 86400
];
$key = ':api:discover:trending:v2.12:range:' . $days;
return true;
})
->values();
} else {
if ($page != 1) {
$res['tags'] = [];
$ids = Cache::remember($key, $ttls[$days], function() use($days) {
$min_id = SnowflakeService::byDate(now()->subDays($days));
return DB::table('statuses')
->select(
'id',
'scope',
'type',
'is_nsfw',
'likes_count',
'created_at'
)
->where('id', '>', $min_id)
->whereNull('uri')
->whereScope('public')
->whereIn('type', [
'photo',
'photo:album',
'video'
])
->whereIsNsfw(false)
->orderBy('likes_count','desc')
->take(30)
->pluck('id');
});
return $res;
}
$key = 'discover:tags:public_feed:'.$hashtag->id.':page:'.$page;
$tags = Cache::remember($key, 43200, function () use ($hashtag, $page, $end) {
return collect(StatusHashtagService::get($hashtag->id, $page, $end))
->filter(function ($tag) {
if (! $tag['status']['local']) {
return false;
}
$filtered = Auth::check() ? UserFilterService::filters(Auth::user()->profile_id) : [];
return true;
})
->values();
});
$res['tags'] = collect($tags)
->filter(function ($tag) {
if (! StatusService::get($tag['status']['id'])) {
return false;
}
$res = $ids->map(function($s) {
return StatusService::get($s);
})->filter(function($s) use($filtered) {
return
$s &&
!in_array($s['account']['id'], $filtered) &&
isset($s['account']);
})->values();
return true;
})
->values();
}
return response()->json($res);
}
return $res;
}
public function trendingHashtags(Request $request)
{
abort_if(!$request->user(), 403);
public function profilesDirectory(Request $request)
{
return redirect('/')->with('statusRedirect', 'The Profile Directory is unavailable at this time.');
}
$res = TrendingHashtagService::getTrending();
return $res;
}
public function profilesDirectoryApi(Request $request)
{
return ['error' => 'Temporarily unavailable.'];
}
public function trendingPlaces(Request $request)
{
return [];
}
public function trendingApi(Request $request)
{
abort_if(config('instance.discover.public') == false && ! $request->user(), 403);
public function myMemories(Request $request)
{
abort_if(!$request->user(), 404);
$pid = $request->user()->profile_id;
abort_if(!$this->config()['memories']['enabled'], 404);
$type = $request->input('type') ?? 'posts';
$this->validate($request, [
'range' => 'nullable|string|in:daily,monthly,yearly',
]);
switch($type) {
case 'posts':
$res = Status::whereProfileId($pid)
->whereDay('created_at', date('d'))
->whereMonth('created_at', date('m'))
->whereYear('created_at', '!=', date('Y'))
->whereNull(['reblog_of_id', 'in_reply_to_id'])
->limit(20)
->pluck('id')
->map(function($id) {
return StatusService::get($id, false);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
break;
$range = $request->input('range');
$days = $range == 'monthly' ? 31 : ($range == 'daily' ? 1 : 365);
$ttls = [
1 => 1500,
31 => 14400,
365 => 86400,
];
$key = ':api:discover:trending:v2.12:range:'.$days;
case 'liked':
$res = Like::whereProfileId($pid)
->whereDay('created_at', date('d'))
->whereMonth('created_at', date('m'))
->whereYear('created_at', '!=', date('Y'))
->orderByDesc('status_id')
->limit(20)
->pluck('status_id')
->map(function($id) {
$status = StatusService::get($id, false);
$status['favourited'] = true;
return $status;
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
break;
}
$ids = Cache::remember($key, $ttls[$days], function () use ($days) {
$min_id = SnowflakeService::byDate(now()->subDays($days));
return $res;
}
return DB::table('statuses')
->select(
'id',
'scope',
'type',
'is_nsfw',
'likes_count',
'created_at'
)
->where('id', '>', $min_id)
->whereNull('uri')
->whereScope('public')
->whereIn('type', [
'photo',
'photo:album',
'video',
])
->whereIsNsfw(false)
->orderBy('likes_count', 'desc')
->take(30)
->pluck('id');
});
public function accountInsightsPopularPosts(Request $request)
{
abort_if(!$request->user(), 404);
$pid = $request->user()->profile_id;
abort_if(!$this->config()['insights']['enabled'], 404);
$posts = Cache::remember('pf:discover:metro2:accinsights:popular:' . $pid, 43200, function() use ($pid) {
return Status::whereProfileId($pid)
->whereNotNull('likes_count')
->orderByDesc('likes_count')
->limit(12)
->pluck('id')
->map(function($id) {
return StatusService::get($id, false);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
});
$filtered = Auth::check() ? UserFilterService::filters(Auth::user()->profile_id) : [];
return $posts;
}
$res = $ids->map(function ($s) {
return StatusService::get($s);
})->filter(function ($s) use ($filtered) {
return
$s &&
! in_array($s['account']['id'], $filtered) &&
isset($s['account']);
})->values();
public function config()
{
$cc = ConfigCacheService::get('config.discover.features');
if($cc) {
return is_string($cc) ? json_decode($cc, true) : $cc;
}
return [
'hashtags' => [
'enabled' => false,
],
'memories' => [
'enabled' => false,
],
'insights' => [
'enabled' => false,
],
'friends' => [
'enabled' => false,
],
'server' => [
'enabled' => false,
'mode' => 'allowlist',
'domains' => []
]
];
}
return response()->json($res);
}
public function serverTimeline(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$this->config()['server']['enabled'], 404);
$pid = $request->user()->profile_id;
$domain = $request->input('domain');
$config = $this->config();
$domains = explode(',', $config['server']['domains']);
abort_unless(in_array($domain, $domains), 400);
public function trendingHashtags(Request $request)
{
abort_if(! $request->user(), 403);
$res = Status::whereNotNull('uri')
->where('uri', 'like', 'https://' . $domain . '%')
->whereNull(['in_reply_to_id', 'reblog_of_id'])
->orderByDesc('id')
->limit(12)
->pluck('id')
->map(function($id) {
return StatusService::get($id);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->values();
return $res;
}
$res = TrendingHashtagService::getTrending();
public function enabledFeatures(Request $request)
{
abort_if(!$request->user(), 404);
return $this->config();
}
return $res;
}
public function updateFeatures(Request $request)
{
abort_if(!$request->user(), 404);
abort_if(!$request->user()->is_admin, 404);
$pid = $request->user()->profile_id;
$this->validate($request, [
'features.friends.enabled' => 'boolean',
'features.hashtags.enabled' => 'boolean',
'features.insights.enabled' => 'boolean',
'features.memories.enabled' => 'boolean',
'features.server.enabled' => 'boolean',
]);
$res = $request->input('features');
if($res['server'] && isset($res['server']['domains']) && !empty($res['server']['domains'])) {
$parts = explode(',', $res['server']['domains']);
$parts = array_filter($parts, function($v) {
$len = strlen($v);
$pos = strpos($v, '.');
$domain = trim($v);
if($pos == false || $pos == ($len + 1)) {
return false;
}
if(!Instance::whereDomain($domain)->exists()) {
return false;
}
return true;
});
$parts = array_slice($parts, 0, 10);
$d = implode(',', array_map('trim', $parts));
$res['server']['domains'] = $d;
}
ConfigCacheService::put('config.discover.features', json_encode($res));
return $res;
}
public function trendingPlaces(Request $request)
{
return [];
}
public function myMemories(Request $request)
{
abort_if(! $request->user(), 404);
$pid = $request->user()->profile_id;
abort_if(! $this->config()['memories']['enabled'], 404);
$type = $request->input('type') ?? 'posts';
switch ($type) {
case 'posts':
$res = Status::whereProfileId($pid)
->whereDay('created_at', date('d'))
->whereMonth('created_at', date('m'))
->whereYear('created_at', '!=', date('Y'))
->whereNull(['reblog_of_id', 'in_reply_to_id'])
->limit(20)
->pluck('id')
->map(function ($id) {
return StatusService::get($id, false);
})
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
break;
case 'liked':
$res = Like::whereProfileId($pid)
->whereDay('created_at', date('d'))
->whereMonth('created_at', date('m'))
->whereYear('created_at', '!=', date('Y'))
->orderByDesc('status_id')
->limit(20)
->pluck('status_id')
->map(function ($id) {
$status = StatusService::get($id, false);
$status['favourited'] = true;
return $status;
})
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
break;
}
return $res;
}
public function accountInsightsPopularPosts(Request $request)
{
abort_if(! $request->user(), 404);
$pid = $request->user()->profile_id;
abort_if(! $this->config()['insights']['enabled'], 404);
$posts = Cache::remember('pf:discover:metro2:accinsights:popular:'.$pid, 43200, function () use ($pid) {
return Status::whereProfileId($pid)
->whereNotNull('likes_count')
->orderByDesc('likes_count')
->limit(12)
->pluck('id')
->map(function ($id) {
return StatusService::get($id, false);
})
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
});
return $posts;
}
public function config()
{
$cc = ConfigCacheService::get('config.discover.features');
if ($cc) {
return is_string($cc) ? json_decode($cc, true) : $cc;
}
return [
'hashtags' => [
'enabled' => false,
],
'memories' => [
'enabled' => false,
],
'insights' => [
'enabled' => false,
],
'friends' => [
'enabled' => false,
],
'server' => [
'enabled' => false,
'mode' => 'allowlist',
'domains' => [],
],
];
}
public function serverTimeline(Request $request)
{
abort_if(! $request->user(), 404);
abort_if(! $this->config()['server']['enabled'], 404);
$pid = $request->user()->profile_id;
$domain = $request->input('domain');
$config = $this->config();
$domains = explode(',', $config['server']['domains']);
abort_unless(in_array($domain, $domains), 400);
$res = Status::whereNotNull('uri')
->where('uri', 'like', 'https://'.$domain.'%')
->whereNull(['in_reply_to_id', 'reblog_of_id'])
->orderByDesc('id')
->limit(12)
->pluck('id')
->map(function ($id) {
return StatusService::get($id);
})
->filter(function ($post) {
return $post && isset($post['account']);
})
->values();
return $res;
}
public function enabledFeatures(Request $request)
{
abort_if(! $request->user(), 404);
return $this->config();
}
public function updateFeatures(Request $request)
{
abort_if(! $request->user(), 404);
abort_if(! $request->user()->is_admin, 404);
$pid = $request->user()->profile_id;
$this->validate($request, [
'features.friends.enabled' => 'boolean',
'features.hashtags.enabled' => 'boolean',
'features.insights.enabled' => 'boolean',
'features.memories.enabled' => 'boolean',
'features.server.enabled' => 'boolean',
]);
$res = $request->input('features');
if ($res['server'] && isset($res['server']['domains']) && ! empty($res['server']['domains'])) {
$parts = explode(',', $res['server']['domains']);
$parts = array_filter($parts, function ($v) {
$len = strlen($v);
$pos = strpos($v, '.');
$domain = trim($v);
if ($pos == false || $pos == ($len + 1)) {
return false;
}
if (! Instance::whereDomain($domain)->exists()) {
return false;
}
return true;
});
$parts = array_slice($parts, 0, 10);
$d = implode(',', array_map('trim', $parts));
$res['server']['domains'] = $d;
}
ConfigCacheService::put('config.discover.features', json_encode($res));
return $res;
}
public function discoverAccountsPopular(Request $request)
{
abort_if(! $request->user(), 403);
$pid = $request->user()->profile_id;
$ids = Cache::remember('api:v1.1:discover:accounts:popular', 14400, function () {
return DB::table('profiles')
->where('is_private', false)
->whereNull('status')
->orderByDesc('profiles.followers_count')
->limit(30)
->get();
});
$filters = UserFilterService::filters($pid);
$asf = AdminShadowFilterService::getHideFromPublicFeedsList();
$ids = $ids->map(function ($profile) {
return AccountService::get($profile->id, true);
})
->filter(function ($profile) {
return $profile && isset($profile['id'], $profile['locked']) && ! $profile['locked'];
})
->filter(function ($profile) use ($pid) {
return $profile['id'] != $pid;
})
->filter(function ($profile) use ($pid) {
return ! FollowerService::follows($pid, $profile['id'], true);
})
->filter(function ($profile) use ($asf) {
return ! in_array($profile['id'], $asf);
})
->filter(function ($profile) use ($filters) {
return ! in_array($profile['id'], $filters);
})
->take(16)
->values();
return response()->json($ids, 200, [], JSON_UNESCAPED_SLASHES);
}
}

Wyświetl plik

@ -2,265 +2,269 @@
namespace App\Http\Controllers;
use App\Jobs\InboxPipeline\{
DeleteWorker,
InboxWorker,
InboxValidator
};
use App\Jobs\RemoteFollowPipeline\RemoteFollowPipeline;
use App\{
AccountLog,
Like,
Profile,
Status,
User
};
use App\Util\Lexer\Nickname;
use App\Util\Webfinger\Webfinger;
use Auth;
use Cache;
use Carbon\Carbon;
use Illuminate\Http\Request;
use League\Fractal;
use App\Util\Site\Nodeinfo;
use App\Util\ActivityPub\{
Helpers,
HttpSignature,
Outbox
};
use Zttp\Zttp;
use App\Jobs\InboxPipeline\DeleteWorker;
use App\Jobs\InboxPipeline\InboxValidator;
use App\Jobs\InboxPipeline\InboxWorker;
use App\Profile;
use App\Services\AccountService;
use App\Services\InstanceService;
use App\Status;
use App\Util\Lexer\Nickname;
use App\Util\Site\Nodeinfo;
use App\Util\Webfinger\Webfinger;
use Cache;
use Illuminate\Http\Request;
class FederationController extends Controller
{
public function nodeinfoWellKnown()
{
abort_if(!config('federation.nodeinfo.enabled'), 404);
return response()->json(Nodeinfo::wellKnown(), 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin','*');
}
public function nodeinfoWellKnown()
{
abort_if(! config('federation.nodeinfo.enabled'), 404);
public function nodeinfo()
{
abort_if(!config('federation.nodeinfo.enabled'), 404);
return response()->json(Nodeinfo::get(), 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin','*');
}
return response()->json(Nodeinfo::wellKnown(), 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin', '*');
}
public function webfinger(Request $request)
{
if (!config('federation.webfinger.enabled') ||
!$request->has('resource') ||
!$request->filled('resource')
) {
return response('', 400);
}
public function nodeinfo()
{
abort_if(! config('federation.nodeinfo.enabled'), 404);
$resource = $request->input('resource');
$domain = config('pixelfed.domain.app');
return response()->json(Nodeinfo::get(), 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin', '*');
}
if(config('federation.activitypub.sharedInbox') &&
$resource == 'acct:' . $domain . '@' . $domain) {
$res = [
'subject' => 'acct:' . $domain . '@' . $domain,
'aliases' => [
'https://' . $domain . '/i/actor'
],
'links' => [
[
'rel' => 'http://webfinger.net/rel/profile-page',
'type' => 'text/html',
'href' => 'https://' . $domain . '/site/kb/instance-actor'
],
[
'rel' => 'self',
'type' => 'application/activity+json',
'href' => 'https://' . $domain . '/i/actor'
]
]
];
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
$hash = hash('sha256', $resource);
$key = 'federation:webfinger:sha256:' . $hash;
if($cached = Cache::get($key)) {
return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES);
}
if(strpos($resource, $domain) == false) {
return response('', 400);
}
$parsed = Nickname::normalizeProfileUrl($resource);
if(empty($parsed) || $parsed['domain'] !== $domain) {
return response('', 400);
}
$username = $parsed['username'];
$profile = Profile::whereNull('domain')->whereUsername($username)->first();
if(!$profile || $profile->status !== null) {
return response('', 400);
}
$webfinger = (new Webfinger($profile))->generate();
Cache::put($key, $webfinger, 1209600);
public function webfinger(Request $request)
{
if (! config('federation.webfinger.enabled') ||
! $request->has('resource') ||
! $request->filled('resource')
) {
return response('', 400);
}
return response()->json($webfinger, 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin','*');
}
$resource = $request->input('resource');
$domain = config('pixelfed.domain.app');
public function hostMeta(Request $request)
{
abort_if(!config('federation.webfinger.enabled'), 404);
if (config('federation.activitypub.sharedInbox') &&
$resource == 'acct:'.$domain.'@'.$domain) {
$res = [
'subject' => 'acct:'.$domain.'@'.$domain,
'aliases' => [
'https://'.$domain.'/i/actor',
],
'links' => [
[
'rel' => 'http://webfinger.net/rel/profile-page',
'type' => 'text/html',
'href' => 'https://'.$domain.'/site/kb/instance-actor',
],
[
'rel' => 'self',
'type' => 'application/activity+json',
'href' => 'https://'.$domain.'/i/actor',
],
],
];
$path = route('well-known.webfinger');
$xml = '<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" type="application/xrd+xml" template="'.$path.'?resource={uri}"/></XRD>';
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
}
$hash = hash('sha256', $resource);
$key = 'federation:webfinger:sha256:'.$hash;
if ($cached = Cache::get($key)) {
return response()->json($cached, 200, [], JSON_UNESCAPED_SLASHES);
}
if (strpos($resource, $domain) == false) {
return response('', 400);
}
$parsed = Nickname::normalizeProfileUrl($resource);
if (empty($parsed) || $parsed['domain'] !== $domain) {
return response('', 400);
}
$username = $parsed['username'];
$profile = Profile::whereNull('domain')->whereUsername($username)->first();
if (! $profile || $profile->status !== null) {
return response('', 400);
}
$webfinger = (new Webfinger($profile))->generate();
Cache::put($key, $webfinger, 1209600);
return response($xml)->header('Content-Type', 'application/xrd+xml');
}
return response()->json($webfinger, 200, [], JSON_UNESCAPED_SLASHES)
->header('Access-Control-Allow-Origin', '*');
}
public function userOutbox(Request $request, $username)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
public function hostMeta(Request $request)
{
abort_if(! config('federation.webfinger.enabled'), 404);
if(!$request->wantsJson()) {
return redirect('/' . $username);
}
$path = route('well-known.webfinger');
$xml = '<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" type="application/xrd+xml" template="'.$path.'?resource={uri}"/></XRD>';
$res = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => 'https://' . config('pixelfed.domain.app') . '/users/' . $username . '/outbox',
'type' => 'OrderedCollection',
'totalItems' => 0,
'orderedItems' => []
];
return response($xml)->header('Content-Type', 'application/xrd+xml');
}
return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/activity+json');
}
public function userOutbox(Request $request, $username)
{
abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404);
public function userInbox(Request $request, $username)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
abort_if(!config('federation.activitypub.inbox'), 404);
if (! $request->wantsJson()) {
return redirect('/'.$username);
}
$headers = $request->headers->all();
$payload = $request->getContent();
if(!$payload || empty($payload)) {
return;
}
$obj = json_decode($payload, true, 8);
if(!isset($obj['id'])) {
return;
}
$domain = parse_url($obj['id'], PHP_URL_HOST);
if(in_array($domain, InstanceService::getBannedDomains())) {
return;
}
$id = AccountService::usernameToId($username);
abort_if(! $id, 404);
$account = AccountService::get($id);
abort_if(! $account || ! isset($account['statuses_count']), 404);
$res = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => 'https://'.config('pixelfed.domain.app').'/users/'.$username.'/outbox',
'type' => 'OrderedCollection',
'totalItems' => $account['statuses_count'] ?? 0,
];
if(isset($obj['type']) && $obj['type'] === 'Delete') {
if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
if($obj['object']['type'] === 'Person') {
if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
return;
}
}
return response(json_encode($res, JSON_UNESCAPED_SLASHES))->header('Content-Type', 'application/activity+json');
}
if($obj['object']['type'] === 'Tombstone') {
if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
return;
}
}
public function userInbox(Request $request, $username)
{
abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404);
abort_if(! config('federation.activitypub.inbox'), 404);
if($obj['object']['type'] === 'Story') {
dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
return;
}
}
return;
} else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
dispatch(new InboxValidator($username, $headers, $payload))->onQueue('follow');
} else {
dispatch(new InboxValidator($username, $headers, $payload))->onQueue('high');
}
return;
}
$headers = $request->headers->all();
$payload = $request->getContent();
if (! $payload || empty($payload)) {
return;
}
$obj = json_decode($payload, true, 8);
if (! isset($obj['id'])) {
return;
}
$domain = parse_url($obj['id'], PHP_URL_HOST);
if (in_array($domain, InstanceService::getBannedDomains())) {
return;
}
public function sharedInbox(Request $request)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
abort_if(!config('federation.activitypub.sharedInbox'), 404);
if (isset($obj['type']) && $obj['type'] === 'Delete') {
if (isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
if ($obj['object']['type'] === 'Person') {
if (Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
$headers = $request->headers->all();
$payload = $request->getContent();
return;
}
}
if(!$payload || empty($payload)) {
return;
}
if ($obj['object']['type'] === 'Tombstone') {
if (Status::whereObjectUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
$obj = json_decode($payload, true, 8);
if(!isset($obj['id'])) {
return;
}
return;
}
}
$domain = parse_url($obj['id'], PHP_URL_HOST);
if(in_array($domain, InstanceService::getBannedDomains())) {
return;
}
if ($obj['object']['type'] === 'Story') {
dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
if(isset($obj['type']) && $obj['type'] === 'Delete') {
if(isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
if($obj['object']['type'] === 'Person') {
if(Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
return;
}
}
return;
}
}
if($obj['object']['type'] === 'Tombstone') {
if(Status::whereObjectUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
return;
}
}
return;
} elseif (isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
dispatch(new InboxValidator($username, $headers, $payload))->onQueue('follow');
} else {
dispatch(new InboxValidator($username, $headers, $payload))->onQueue('high');
}
if($obj['object']['type'] === 'Story') {
dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
return;
}
}
return;
} else if( isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
dispatch(new InboxWorker($headers, $payload))->onQueue('follow');
} else {
dispatch(new InboxWorker($headers, $payload))->onQueue('shared');
}
return;
}
}
public function userFollowing(Request $request, $username)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
public function sharedInbox(Request $request)
{
abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404);
abort_if(! config('federation.activitypub.sharedInbox'), 404);
$obj = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $request->getUri(),
'type' => 'OrderedCollectionPage',
'totalItems' => 0,
'orderedItems' => []
];
return response()->json($obj);
}
$headers = $request->headers->all();
$payload = $request->getContent();
public function userFollowers(Request $request, $username)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
if (! $payload || empty($payload)) {
return;
}
$obj = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $request->getUri(),
'type' => 'OrderedCollectionPage',
'totalItems' => 0,
'orderedItems' => []
];
$obj = json_decode($payload, true, 8);
if (! isset($obj['id'])) {
return;
}
return response()->json($obj);
}
$domain = parse_url($obj['id'], PHP_URL_HOST);
if (in_array($domain, InstanceService::getBannedDomains())) {
return;
}
if (isset($obj['type']) && $obj['type'] === 'Delete') {
if (isset($obj['object']) && isset($obj['object']['type']) && isset($obj['object']['id'])) {
if ($obj['object']['type'] === 'Person') {
if (Profile::whereRemoteUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('inbox');
return;
}
}
if ($obj['object']['type'] === 'Tombstone') {
if (Status::whereObjectUrl($obj['object']['id'])->exists()) {
dispatch(new DeleteWorker($headers, $payload))->onQueue('delete');
return;
}
}
if ($obj['object']['type'] === 'Story') {
dispatch(new DeleteWorker($headers, $payload))->onQueue('story');
return;
}
}
return;
} elseif (isset($obj['type']) && in_array($obj['type'], ['Follow', 'Accept'])) {
dispatch(new InboxWorker($headers, $payload))->onQueue('follow');
} else {
dispatch(new InboxWorker($headers, $payload))->onQueue('shared');
}
}
public function userFollowing(Request $request, $username)
{
abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404);
$id = AccountService::usernameToId($username);
abort_if(! $id, 404);
$account = AccountService::get($id);
abort_if(! $account || ! isset($account['following_count']), 404);
$obj = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $request->getUri(),
'type' => 'OrderedCollection',
'totalItems' => $account['following_count'] ?? 0,
];
return response()->json($obj)->header('Content-Type', 'application/activity+json');
}
public function userFollowers(Request $request, $username)
{
abort_if(! (bool) config_cache('federation.activitypub.enabled'), 404);
$id = AccountService::usernameToId($username);
abort_if(! $id, 404);
$account = AccountService::get($id);
abort_if(! $account || ! isset($account['followers_count']), 404);
$obj = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $request->getUri(),
'type' => 'OrderedCollection',
'totalItems' => $account['followers_count'] ?? 0,
];
return response()->json($obj)->header('Content-Type', 'application/activity+json');
}
}

Wyświetl plik

@ -0,0 +1,16 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HealthCheckController extends Controller
{
public function get(Request $request)
{
return response('OK')->withHeaders([
'Content-Type' => 'text/plain',
'Cache-Control' => 'max-age=0, must-revalidate, no-cache, no-store'
]);
}
}

Wyświetl plik

@ -17,7 +17,7 @@ trait Instagram
{
public function instagram()
{
if(config_cache('pixelfed.import.instagram.enabled') != true) {
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
return view('settings.import.instagram.home');
@ -25,6 +25,9 @@ trait Instagram
public function instagramStart(Request $request)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$completed = ImportJob::whereProfileId(Auth::user()->profile->id)
->whereService('instagram')
->whereNotNull('completed_at')
@ -38,6 +41,9 @@ trait Instagram
protected function instagramRedirectOrNew()
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$profile = Auth::user()->profile;
$exists = ImportJob::whereProfileId($profile->id)
->whereService('instagram')
@ -61,6 +67,9 @@ trait Instagram
public function instagramStepOne(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$profile = Auth::user()->profile;
$job = ImportJob::whereProfileId($profile->id)
->whereNull('completed_at')
@ -72,6 +81,9 @@ trait Instagram
public function instagramStepOneStore(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$max = 'max:' . config('pixelfed.import.instagram.limits.size');
$this->validate($request, [
'media.*' => 'required|mimes:bin,jpeg,png,gif|'.$max,
@ -93,7 +105,7 @@ trait Instagram
continue;
}
$storagePath = "import/{$job->uuid}";
$path = $v->store($storagePath);
$path = $v->storePublicly($storagePath);
DB::transaction(function() use ($profile, $job, $path, $original) {
$data = new ImportData;
$data->profile_id = $profile->id;
@ -114,6 +126,9 @@ trait Instagram
public function instagramStepTwo(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$profile = Auth::user()->profile;
$job = ImportJob::whereProfileId($profile->id)
->whereNull('completed_at')
@ -125,6 +140,9 @@ trait Instagram
public function instagramStepTwoStore(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$this->validate($request, [
'media' => 'required|file|max:1000'
]);
@ -141,7 +159,7 @@ trait Instagram
return abort(500);
}
$storagePath = "import/{$job->uuid}";
$path = $media->store($storagePath);
$path = $media->storePublicly($storagePath);
$job->media_json = $path;
$job->stage = 3;
$job->save();
@ -150,6 +168,9 @@ trait Instagram
public function instagramStepThree(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$profile = Auth::user()->profile;
$job = ImportJob::whereProfileId($profile->id)
->whereService('instagram')
@ -162,6 +183,9 @@ trait Instagram
public function instagramStepThreeStore(Request $request, $uuid)
{
if((bool) config_cache('pixelfed.import.instagram.enabled') != true) {
abort(404, 'Feature not enabled');
}
$profile = Auth::user()->profile;
try {

Wyświetl plik

@ -0,0 +1,309 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\ImportPost;
use App\Services\ImportService;
use App\Services\StatusService;
use App\Http\Resources\ImportStatus;
use App\Follower;
use App\User;
class ImportPostController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function getConfig(Request $request)
{
return [
'enabled' => config('import.instagram.enabled'),
'limits' => [
'max_posts' => config('import.instagram.limits.max_posts'),
'max_attempts' => config('import.instagram.limits.max_attempts'),
],
'allow_video_posts' => config('import.instagram.allow_video_posts'),
'permissions' => [
'admins_only' => config('import.instagram.permissions.admins_only'),
'admin_follows_only' => config('import.instagram.permissions.admin_follows_only'),
'min_account_age' => config('import.instagram.permissions.min_account_age'),
'min_follower_count' => config('import.instagram.permissions.min_follower_count'),
],
'allowed' => $this->checkPermissions($request, false)
];
}
public function getProcessingCount(Request $request)
{
abort_unless(config('import.instagram.enabled'), 404);
$processing = ImportPost::whereProfileId($request->user()->profile_id)
->whereNull('status_id')
->whereSkipMissingMedia(false)
->count();
$finished = ImportPost::whereProfileId($request->user()->profile_id)
->whereNotNull('status_id')
->whereSkipMissingMedia(false)
->count();
return response()->json([
'processing_count' => $processing,
'finished_count' => $finished,
]);
}
public function getImportedFiles(Request $request)
{
abort_unless(config('import.instagram.enabled'), 404);
return response()->json(
ImportService::getImportedFiles($request->user()->profile_id),
200,
[],
JSON_UNESCAPED_SLASHES
);
}
public function getImportedPosts(Request $request)
{
abort_unless(config('import.instagram.enabled'), 404);
return ImportStatus::collection(
ImportPost::whereProfileId($request->user()->profile_id)
->has('status')
->cursorPaginate(9)
);
}
public function formatHashtags($val = false)
{
if(!$val || !strlen($val)) {
return null;
}
$groupedHashtagRegex = '/#\w+(?=#)/';
return preg_replace($groupedHashtagRegex, '$0 ', $val);
}
public function store(Request $request)
{
abort_unless(config('import.instagram.enabled'), 404);
$this->checkPermissions($request);
$uid = $request->user()->id;
$pid = $request->user()->profile_id;
foreach($request->input('files') as $file) {
$media = $file['media'];
$c = collect($media);
$postHash = hash('sha256', $c->toJson());
$exts = $c->map(function($m) {
$fn = last(explode('/', $m['uri']));
return last(explode('.', $fn));
});
$postType = 'photo';
if($exts->count() > 1) {
if($exts->contains('mp4')) {
if($exts->contains('jpg', 'png')) {
$postType = 'photo:video:album';
} else {
$postType = 'video:album';
}
} else {
$postType = 'photo:album';
}
} else {
if(in_array($exts[0], ['jpg', 'png'])) {
$postType = 'photo';
} else if(in_array($exts[0], ['mp4'])) {
$postType = 'video';
}
}
$ip = new ImportPost;
$ip->user_id = $uid;
$ip->profile_id = $pid;
$ip->post_hash = $postHash;
$ip->service = 'instagram';
$ip->post_type = $postType;
$ip->media_count = $c->count();
$ip->media = $c->map(function($m) {
return [
'uri' => $m['uri'],
'title' => $this->formatHashtags($m['title']),
'creation_timestamp' => $m['creation_timestamp']
];
})->toArray();
$ip->caption = $c->count() > 1 ? $this->formatHashtags($file['title']) : $this->formatHashtags($ip->media[0]['title']);
$ip->filename = last(explode('/', $ip->media[0]['uri']));
$ip->metadata = $c->map(function($m) {
return [
'uri' => $m['uri'],
'media_metadata' => isset($m['media_metadata']) ? $m['media_metadata'] : null
];
})->toArray();
$ip->creation_date = $c->count() > 1 ? now()->parse($file['creation_timestamp']) : now()->parse($media[0]['creation_timestamp']);
$ip->creation_year = now()->parse($ip->creation_date)->format('y');
$ip->creation_month = now()->parse($ip->creation_date)->format('m');
$ip->creation_day = now()->parse($ip->creation_date)->format('d');
$ip->save();
ImportService::getImportedFiles($pid, true);
ImportService::getPostCount($pid, true);
}
return [
'msg' => 'Success'
];
}
public function storeMedia(Request $request)
{
abort_unless(config('import.instagram.enabled'), 404);
$this->checkPermissions($request);
$mimes = config('import.instagram.allow_video_posts') ? 'mimetypes:image/png,image/jpeg,video/mp4' : 'mimetypes:image/png,image/jpeg';
$this->validate($request, [
'file' => 'required|array|max:10',
'file.*' => [
'required',
'file',
$mimes,
'max:' . config_cache('pixelfed.max_photo_size')
]
]);
foreach($request->file('file') as $file) {
$fileName = $file->getClientOriginalName();
$file->storeAs('imports/' . $request->user()->id . '/', $fileName);
}
ImportService::getImportedFiles($request->user()->profile_id, true);
return [
'msg' => 'Success'
];
}
protected function checkPermissions($request, $abortOnFail = true)
{
$user = $request->user();
if($abortOnFail) {
abort_unless(config('import.instagram.enabled'), 404);
}
if($user->is_admin) {
if(!$abortOnFail) {
return true;
} else {
return;
}
}
$admin = User::whereIsAdmin(true)->first();
if(config('import.instagram.permissions.admins_only')) {
if($abortOnFail) {
abort_unless($user->is_admin, 404, 'Only admins can use this feature.');
} else {
if(!$user->is_admin) {
return false;
}
}
}
if(config('import.instagram.permissions.admin_follows_only')) {
$exists = Follower::whereProfileId($admin->profile_id)
->whereFollowingId($user->profile_id)
->exists();
if($abortOnFail) {
abort_unless(
$exists,
404,
'Only admins, and accounts they follow can use this feature'
);
} else {
if(!$exists) {
return false;
}
}
}
if(config('import.instagram.permissions.min_account_age')) {
$res = $user->created_at->lt(
now()->subDays(config('import.instagram.permissions.min_account_age'))
);
if($abortOnFail) {
abort_unless(
$res,
404,
'Your account is too new to use this feature'
);
} else {
if(!$res) {
return false;
}
}
}
if(config('import.instagram.permissions.min_follower_count')) {
$res = Follower::whereFollowingId($user->profile_id)->count() >= config('import.instagram.permissions.min_follower_count');
if($abortOnFail) {
abort_unless(
$res,
404,
'You don\'t have enough followers to use this feature'
);
} else {
if(!$res) {
return false;
}
}
}
if(intval(config('import.instagram.limits.max_posts')) > 0) {
$res = ImportService::getPostCount($user->profile_id) >= intval(config('import.instagram.limits.max_posts'));
if($abortOnFail) {
abort_if(
$res,
404,
'You have reached the limit of post imports and cannot import any more posts'
);
} else {
if($res) {
return false;
}
}
}
if(intval(config('import.instagram.limits.max_attempts')) > 0) {
$res = ImportService::getAttempts($user->profile_id) >= intval(config('import.instagram.limits.max_attempts'));
if($abortOnFail) {
abort_if(
$res,
404,
'You have reached the limit of post import attempts and cannot import any more posts'
);
} else {
if($res) {
return false;
}
}
}
if(!$abortOnFail) {
return true;
}
}
}

Wyświetl plik

@ -25,7 +25,61 @@ class InstanceActorController extends Controller
public function outbox()
{
$res = json_encode([
'@context' => 'https://www.w3.org/ns/activitystreams',
"@context" => [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
[
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
"toot" => "http://joinmastodon.org/ns#",
"featured" => [
"@id" => "toot:featured",
"@type" => "@id"
],
"featuredTags" => [
"@id" => "toot:featuredTags",
"@type" => "@id"
],
"alsoKnownAs" => [
"@id" => "as:alsoKnownAs",
"@type" => "@id"
],
"movedTo" => [
"@id" => "as:movedTo",
"@type" => "@id"
],
"schema" => "http://schema.org#",
"PropertyValue" => "schema:PropertyValue",
"value" => "schema:value",
"discoverable" => "toot:discoverable",
"Device" => "toot:Device",
"Ed25519Signature" => "toot:Ed25519Signature",
"Ed25519Key" => "toot:Ed25519Key",
"Curve25519Key" => "toot:Curve25519Key",
"EncryptedMessage" => "toot:EncryptedMessage",
"publicKeyBase64" => "toot:publicKeyBase64",
"deviceId" => "toot:deviceId",
"claim" => [
"@type" => "@id",
"@id" => "toot:claim"
],
"fingerprintKey" => [
"@type" => "@id",
"@id" => "toot:fingerprintKey"
],
"identityKey" => [
"@type" => "@id",
"@id" => "toot:identityKey"
],
"devices" => [
"@type" => "@id",
"@id" => "toot:devices"
],
"messageFranking" => "toot:messageFranking",
"messageType" => "toot:messageType",
"cipherText" => "toot:cipherText",
"suspended" => "toot:suspended"
]
],
'id' => config('app.url') . '/i/actor/outbox',
'type' => 'OrderedCollection',
'totalItems' => 0,

Wyświetl plik

@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\DirectoryProfile;
use App\Profile;
use Illuminate\Http\Request;
class LandingController extends Controller
{
public function directoryRedirect(Request $request)
{
if ($request->user()) {
return redirect('/');
}
abort_if((bool) config_cache('instance.landing.show_directory') == false, 404);
return view('site.index');
}
public function exploreRedirect(Request $request)
{
if ($request->user()) {
return redirect('/');
}
abort_if((bool) config_cache('instance.landing.show_explore') == false, 404);
return view('site.index');
}
public function getDirectoryApi(Request $request)
{
abort_if((bool) config_cache('instance.landing.show_directory') == false, 404);
return DirectoryProfile::collection(
Profile::whereNull('domain')
->whereIsSuggestable(true)
->orderByDesc('updated_at')
->cursorPaginate(20)
);
}
}

Wyświetl plik

@ -25,8 +25,7 @@ class LikeController extends Controller
'item' => 'required|integer|min:1',
]);
// API deprecated
return;
abort(422, 'Deprecated API Endpoint');
$user = Auth::user();
$profile = $user->profile;
@ -34,7 +33,7 @@ class LikeController extends Controller
if (Like::whereStatusId($status->id)->whereProfileId($profile->id)->exists()) {
$like = Like::whereProfileId($profile->id)->whereStatusId($status->id)->firstOrFail();
UnlikePipeline::dispatch($like);
UnlikePipeline::dispatch($like)->onQueue('feed');
} else {
abort_if(
Like::whereProfileId($user->profile_id)
@ -60,7 +59,7 @@ class LikeController extends Controller
]) == false;
$like->save();
$status->save();
LikePipeline::dispatch($like);
LikePipeline::dispatch($like)->onQueue('feed');
}
}

Wyświetl plik

@ -2,26 +2,31 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth, Storage, URL;
use App\Media;
use Image as Intervention;
use App\Jobs\ImageOptimizePipeline\ImageOptimize;
use Illuminate\Http\Request;
class MediaController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index(Request $request)
{
//return view('settings.drive.index');
abort(404);
}
public function index(Request $request)
{
//return view('settings.drive.index');
}
public function composeUpdate(Request $request, $id)
{
public function composeUpdate(Request $request, $id)
{
abort(400, 'Endpoint deprecated');
}
}
public function fallbackRedirect(Request $request, $pid, $mhash, $uhash, $f)
{
abort_if(! (bool) config_cache('pixelfed.cloud_storage'), 404);
$path = 'public/m/_v2/'.$pid.'/'.$mhash.'/'.$uhash.'/'.$f;
$media = Media::whereProfileId($pid)
->whereMediaPath($path)
->whereNotNull('cdn_url')
->firstOrFail();
return redirect()->away($media->cdn_url);
}
}

Wyświetl plik

@ -0,0 +1,231 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\ParentalControls;
use App\Models\UserRoles;
use App\Profile;
use App\User;
use App\Http\Controllers\Auth\RegisterController;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use App\Services\UserRoleService;
use App\Jobs\ParentalControlsPipeline\DispatchChildInvitePipeline;
class ParentalControlsController extends Controller
{
public function authPreflight($request, $maxUserCheck = false, $authCheck = true)
{
if($authCheck) {
abort_unless($request->user(), 404);
abort_unless($request->user()->has_roles === 0, 404);
}
abort_unless(config('instance.parental_controls.enabled'), 404);
if(config_cache('pixelfed.open_registration') == false) {
abort_if(config('instance.parental_controls.limits.respect_open_registration'), 404);
}
if($maxUserCheck == true) {
$hasLimit = config('pixelfed.enforce_max_users');
if($hasLimit) {
$count = User::where(function($q){ return $q->whereNull('status')->orWhereNotIn('status', ['deleted','delete']); })->count();
$limit = (int) config('pixelfed.max_users');
abort_if($limit && $limit <= $count, 404);
}
}
}
public function index(Request $request)
{
$this->authPreflight($request);
$children = ParentalControls::whereParentId($request->user()->id)->latest()->paginate(5);
return view('settings.parental-controls.index', compact('children'));
}
public function add(Request $request)
{
$this->authPreflight($request, true);
return view('settings.parental-controls.add');
}
public function view(Request $request, $id)
{
$this->authPreflight($request);
$uid = $request->user()->id;
$pc = ParentalControls::whereParentId($uid)->findOrFail($id);
return view('settings.parental-controls.manage', compact('pc'));
}
public function update(Request $request, $id)
{
$this->authPreflight($request);
$uid = $request->user()->id;
$ff = $this->requestFormFields($request);
$pc = ParentalControls::whereParentId($uid)->findOrFail($id);
$pc->permissions = $ff;
$pc->save();
$roles = UserRoleService::mapActions($pc->child_id, $ff);
if(isset($roles['account-force-private'])) {
$c = Profile::whereUserId($pc->child_id)->first();
$c->is_private = $roles['account-force-private'];
$c->save();
}
UserRoles::whereUserId($pc->child_id)->update(['roles' => $roles]);
return redirect($pc->manageUrl() . '?permissions');
}
public function store(Request $request)
{
$this->authPreflight($request, true);
$this->validate($request, [
'email' => 'required|email|unique:parental_controls,email|unique:users,email',
]);
$state = $this->requestFormFields($request);
$pc = new ParentalControls;
$pc->parent_id = $request->user()->id;
$pc->email = $request->input('email');
$pc->verify_code = str_random(32);
$pc->permissions = $state;
$pc->save();
DispatchChildInvitePipeline::dispatch($pc);
return redirect($pc->manageUrl());
}
public function inviteRegister(Request $request, $id, $code)
{
if($request->user()) {
$title = 'You cannot complete this action on this device.';
$body = 'Please log out or use a different device or browser to complete the invitation registration.';
return view('errors.custom', compact('title', 'body'));
}
$this->authPreflight($request, true, false);
$pc = ParentalControls::whereRaw('verify_code = BINARY ?', $code)->whereNull(['email_verified_at', 'child_id'])->findOrFail($id);
abort_unless(User::whereId($pc->parent_id)->exists(), 404);
return view('settings.parental-controls.invite-register-form', compact('pc'));
}
public function inviteRegisterStore(Request $request, $id, $code)
{
if($request->user()) {
$title = 'You cannot complete this action on this device.';
$body = 'Please log out or use a different device or browser to complete the invitation registration.';
return view('errors.custom', compact('title', 'body'));
}
$this->authPreflight($request, true, false);
$pc = ParentalControls::whereRaw('verify_code = BINARY ?', $code)->whereNull('email_verified_at')->findOrFail($id);
$fields = $request->all();
$fields['email'] = $pc->email;
$defaults = UserRoleService::defaultRoles();
$validator = (new RegisterController)->validator($fields);
$valid = $validator->validate();
abort_if(!$valid, 404);
event(new Registered($user = (new RegisterController)->create($fields)));
sleep(5);
$user->has_roles = true;
$user->parent_id = $pc->parent_id;
if(config('instance.parental_controls.limits.auto_verify_email')) {
$user->email_verified_at = now();
$user->save();
sleep(3);
} else {
$user->save();
sleep(3);
}
$ur = UserRoles::updateOrCreate([
'user_id' => $user->id,
],[
'roles' => UserRoleService::mapInvite($user->id, $pc->permissions)
]);
$pc->email_verified_at = now();
$pc->child_id = $user->id;
$pc->save();
sleep(2);
Auth::guard()->login($user);
return redirect('/i/web');
}
public function cancelInvite(Request $request, $id)
{
$this->authPreflight($request);
$pc = ParentalControls::whereParentId($request->user()->id)
->whereNull(['email_verified_at', 'child_id'])
->findOrFail($id);
return view('settings.parental-controls.delete-invite', compact('pc'));
}
public function cancelInviteHandle(Request $request, $id)
{
$this->authPreflight($request);
$pc = ParentalControls::whereParentId($request->user()->id)
->whereNull(['email_verified_at', 'child_id'])
->findOrFail($id);
$pc->delete();
return redirect('/settings/parental-controls');
}
public function stopManaging(Request $request, $id)
{
$this->authPreflight($request);
$pc = ParentalControls::whereParentId($request->user()->id)
->whereNotNull(['email_verified_at', 'child_id'])
->findOrFail($id);
return view('settings.parental-controls.stop-managing', compact('pc'));
}
public function stopManagingHandle(Request $request, $id)
{
$this->authPreflight($request);
$pc = ParentalControls::whereParentId($request->user()->id)
->whereNotNull(['email_verified_at', 'child_id'])
->findOrFail($id);
$pc->child()->update([
'has_roles' => false,
'parent_id' => null,
]);
$pc->delete();
return redirect('/settings/parental-controls');
}
protected function requestFormFields($request)
{
$state = [];
$fields = [
'post',
'comment',
'like',
'share',
'follow',
'bookmark',
'story',
'collection',
'discovery_feeds',
'dms',
'federation',
'hide_network',
'private',
'hide_cw'
];
foreach ($fields as $field) {
$state[$field] = $request->input($field) == 'on';
}
return $state;
}
}

Wyświetl plik

@ -2,37 +2,41 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\ConfigCache;
use Storage;
use App\Services\AccountService;
use App\Services\StatusService;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Cache;
use Storage;
use App\Status;
use App\User;
class PixelfedDirectoryController extends Controller
{
public function get(Request $request)
{
if(!$request->filled('sk')) {
if (! $request->filled('sk')) {
abort(404);
}
if(!config_cache('pixelfed.directory.submission-key')) {
if (! config_cache('pixelfed.directory.submission-key')) {
abort(404);
}
if(!hash_equals(config_cache('pixelfed.directory.submission-key'), $request->input('sk'))) {
if (! hash_equals(config_cache('pixelfed.directory.submission-key'), $request->input('sk'))) {
abort(403);
}
$res = $this->buildListing();
return response()->json($res, 200, [], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
return response()->json($res, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function buildListing()
{
$res = config_cache('pixelfed.directory');
if($res) {
if ($res) {
$res = is_string($res) ? json_decode($res, true) : $res;
}
@ -41,70 +45,71 @@ class PixelfedDirectoryController extends Controller
$res['_ts'] = config_cache('pixelfed.directory.submission-ts');
$res['version'] = config_cache('pixelfed.version');
if(empty($res['summary'])) {
if (empty($res['summary'])) {
$summary = ConfigCache::whereK('app.short_description')->pluck('v');
$res['summary'] = $summary ? $summary[0] : null;
}
if(isset($res['admin'])) {
if (isset($res['admin'])) {
$res['admin'] = AccountService::get($res['admin'], true);
}
if(isset($res['banner_image']) && !empty($res['banner_image'])) {
if (isset($res['banner_image']) && ! empty($res['banner_image'])) {
$res['banner_image'] = url(Storage::url($res['banner_image']));
}
if(isset($res['favourite_posts'])) {
$res['favourite_posts'] = collect($res['favourite_posts'])->map(function($id) {
if (isset($res['favourite_posts'])) {
$res['favourite_posts'] = collect($res['favourite_posts'])->map(function ($id) {
return StatusService::get($id);
})
->filter(function($post) {
return $post && isset($post['account']);
})
->map(function($post) {
return [
'avatar' => $post['account']['avatar'],
'display_name' => $post['account']['display_name'],
'username' => $post['account']['username'],
'media' => $post['media_attachments'][0]['url'],
'url' => $post['url']
];
})
->values();
->filter(function ($post) {
return $post && isset($post['account']);
})
->map(function ($post) {
return [
'avatar' => $post['account']['avatar'],
'display_name' => $post['account']['display_name'],
'username' => $post['account']['username'],
'media' => $post['media_attachments'][0]['url'],
'url' => $post['url'],
];
})
->values();
}
$guidelines = ConfigCache::whereK('app.rules')->first();
if($guidelines) {
if ($guidelines) {
$res['community_guidelines'] = json_decode($guidelines->v, true);
}
$openRegistration = ConfigCache::whereK('pixelfed.open_registration')->first();
if($openRegistration) {
$res['open_registration'] = (bool) $openRegistration;
}
$openRegistration = (bool) config_cache('pixelfed.open_registration');
$res['open_registration'] = $openRegistration;
$curatedOnboarding = (bool) config_cache('instance.curated_registration.enabled');
$res['curated_onboarding'] = $curatedOnboarding;
$oauthEnabled = ConfigCache::whereK('pixelfed.oauth_enabled')->first();
if($oauthEnabled) {
if ($oauthEnabled) {
$keys = file_exists(storage_path('oauth-public.key')) && file_exists(storage_path('oauth-private.key'));
$res['oauth_enabled'] = (bool) $oauthEnabled && $keys;
}
$activityPubEnabled = ConfigCache::whereK('federation.activitypub.enabled')->first();
if($activityPubEnabled) {
if ($activityPubEnabled) {
$res['activitypub_enabled'] = (bool) $activityPubEnabled;
}
$res['feature_config'] = [
'media_types' => Str::of(config_cache('pixelfed.media_types'))->explode(','),
'image_quality' => config_cache('pixelfed.image_quality'),
'optimize_image' => config_cache('pixelfed.optimize_image'),
'optimize_image' => (bool) config_cache('pixelfed.optimize_image'),
'max_photo_size' => config_cache('pixelfed.max_photo_size'),
'max_caption_length' => config_cache('pixelfed.max_caption_length'),
'max_altext_length' => config_cache('pixelfed.max_altext_length'),
'enforce_account_limit' => config_cache('pixelfed.enforce_account_limit'),
'enforce_account_limit' => (bool) config_cache('pixelfed.enforce_account_limit'),
'max_account_size' => config_cache('pixelfed.max_account_size'),
'max_album_length' => config_cache('pixelfed.max_album_length'),
'account_deletion' => config_cache('pixelfed.account_deletion'),
'account_deletion' => (bool) config_cache('pixelfed.account_deletion'),
];
$res['is_eligible'] = $this->validVal($res, 'admin') &&
@ -114,29 +119,36 @@ class PixelfedDirectoryController extends Controller
$this->validVal($res, 'privacy_pledge') &&
$this->validVal($res, 'location');
if(config_cache('pixelfed.directory.testimonials')) {
if (config_cache('pixelfed.directory.testimonials')) {
$res['testimonials'] = collect(json_decode(config_cache('pixelfed.directory.testimonials'), true))
->map(function($testimonial) {
->map(function ($testimonial) {
$profile = AccountService::get($testimonial['profile_id']);
return [
'profile' => [
'username' => $profile['username'],
'display_name' => $profile['display_name'],
'avatar' => $profile['avatar'],
'created_at' => $profile['created_at']
'created_at' => $profile['created_at'],
],
'body' => $testimonial['body']
'body' => $testimonial['body'],
];
});
}
$res['features_enabled'] = [
'stories' => (bool) config_cache('instance.stories.enabled')
'stories' => (bool) config_cache('instance.stories.enabled'),
];
$statusesCount = Cache::remember('api:nodeinfo:statuses', 21600, function() {
return Status::whereLocal(true)->count();
});
$usersCount = Cache::remember('api:nodeinfo:users', 43200, function() {
return User::count();
});
$res['stats'] = [
'user_count' => \App\User::count(),
'post_count' => \App\Status::whereNull('uri')->count(),
'user_count' => (int) $usersCount,
'post_count' => (int) $statusesCount,
];
$res['primary_locale'] = config('app.locale');
@ -149,19 +161,18 @@ class PixelfedDirectoryController extends Controller
protected function validVal($res, $val, $count = false, $minLen = false)
{
if(!isset($res[$val])) {
if (! isset($res[$val])) {
return false;
}
if($count) {
if ($count) {
return count($res[$val]) >= $count;
}
if($minLen) {
if ($minLen) {
return strlen($res[$val]) >= $minLen;
}
return $res[$val];
}
}

Wyświetl plik

@ -0,0 +1,93 @@
<?php
namespace App\Http\Controllers;
use App\Models\ProfileAlias;
use App\Models\ProfileMigration;
use App\Services\AccountService;
use App\Services\WebfingerService;
use App\Util\Lexer\Nickname;
use Cache;
use Illuminate\Http\Request;
class ProfileAliasController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index(Request $request)
{
$aliases = $request->user()->profile->aliases;
return view('settings.aliases.index', compact('aliases'));
}
public function store(Request $request)
{
$this->validate($request, [
'acct' => 'required',
]);
$acct = $request->input('acct');
$nn = Nickname::normalizeProfileUrl($acct);
if (! $nn) {
return back()->with('error', 'Invalid account alias.');
}
if ($nn['domain'] === config('pixelfed.domain.app')) {
if (strtolower($nn['username']) == ($request->user()->username)) {
return back()->with('error', 'You cannot add an alias to your own account.');
}
}
if ($request->user()->profile->aliases->count() >= 3) {
return back()->with('error', 'You can only add 3 account aliases.');
}
$webfingerService = WebfingerService::lookup($acct);
$webfingerUrl = WebfingerService::rawGet($acct);
if (! $webfingerService || ! isset($webfingerService['url']) || ! $webfingerUrl || empty($webfingerUrl)) {
return back()->with('error', 'Invalid account, cannot add alias at this time.');
}
$alias = new ProfileAlias;
$alias->profile_id = $request->user()->profile_id;
$alias->acct = $acct;
$alias->uri = $webfingerUrl;
$alias->save();
Cache::forget('pf:activitypub:user-object:by-id:'.$request->user()->profile_id);
return back()->with('status', 'Successfully added alias!');
}
public function delete(Request $request)
{
$this->validate($request, [
'acct' => 'required',
'id' => 'required|exists:profile_aliases',
]);
$pid = $request->user()->profile_id;
$acct = $request->input('acct');
$alias = ProfileAlias::where('profile_id', $pid)
->where('acct', $acct)
->findOrFail($request->input('id'));
$migration = ProfileMigration::whereProfileId($pid)
->whereAcct($acct)
->first();
if ($migration) {
$request->user()->profile->update([
'moved_to_profile_id' => null,
]);
}
$alias->delete();
Cache::forget('pf:activitypub:user-object:by-id:'.$pid);
AccountService::del($pid);
return back()->with('status', 'Successfully deleted alias!');
}
}

Wyświetl plik

@ -2,298 +2,387 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use Cache;
use DB;
use View;
use App\AccountInterstitial;
use App\Follower;
use App\FollowRequest;
use App\Profile;
use App\Story;
use App\User;
use App\UserFilter;
use League\Fractal;
use App\Services\AccountService;
use App\Services\FollowerService;
use App\Services\StatusService;
use App\Util\Lexer\Nickname;
use App\Util\Webfinger\Webfinger;
use App\Transformer\ActivityPub\ProfileOutbox;
use App\Status;
use App\Story;
use App\Transformer\ActivityPub\ProfileTransformer;
use App\User;
use App\UserFilter;
use App\UserSetting;
use Auth;
use Cache;
use Illuminate\Http\Request;
use League\Fractal;
use View;
class ProfileController extends Controller
{
public function show(Request $request, $username)
{
// redirect authed users to Metro 2.0
if($request->user()) {
// unless they force static view
if(!$request->has('fs') || $request->input('fs') != '1') {
$pid = AccountService::usernameToId($username);
if($pid) {
return redirect('/i/web/profile/' . $pid);
}
}
}
public function show(Request $request, $username)
{
if ($request->wantsJson() && (bool) config_cache('federation.activitypub.enabled')) {
$user = $this->getCachedUser($username, true);
abort_if(! $user, 404, 'Not found');
$user = Profile::whereNull('domain')
->whereNull('status')
->whereUsername($username)
->firstOrFail();
return $this->showActivityPub($request, $user);
}
if($request->wantsJson() && config_cache('federation.activitypub.enabled')) {
return $this->showActivityPub($request, $user);
}
return $this->buildProfile($request, $user);
}
// redirect authed users to Metro 2.0
if ($request->user()) {
// unless they force static view
if (! $request->has('fs') || $request->input('fs') != '1') {
$pid = AccountService::usernameToId($username);
if ($pid) {
return redirect('/i/web/profile/'.$pid);
}
}
}
protected function buildProfile(Request $request, $user)
{
$username = $user->username;
$loggedIn = Auth::check();
$isPrivate = false;
$isBlocked = false;
if(!$loggedIn) {
$key = 'profile:settings:' . $user->id;
$ttl = now()->addHours(6);
$settings = Cache::remember($key, $ttl, function() use($user) {
return $user->user->settings;
});
$user = $this->getCachedUser($username);
if ($user->is_private == true) {
$profile = null;
return view('profile.private', compact('user'));
}
abort_unless($user, 404);
$owner = false;
$is_following = false;
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$user->id, 3600, function () use ($user) {
$exists = AccountInterstitial::whereUserId($user->user_id)->where('is_spam', 1)->count();
if ($exists) {
return true;
}
$profile = $user;
$settings = [
'crawlable' => $settings->crawlable,
'following' => [
'count' => $settings->show_profile_following_count,
'list' => $settings->show_profile_following
],
'followers' => [
'count' => $settings->show_profile_follower_count,
'list' => $settings->show_profile_followers
]
];
return view('profile.show', compact('profile', 'settings'));
} else {
$key = 'profile:settings:' . $user->id;
$ttl = now()->addHours(6);
$settings = Cache::remember($key, $ttl, function() use($user) {
return $user->user->settings;
});
return false;
});
if ($aiCheck) {
return redirect('/login');
}
if ($user->is_private == true) {
$isPrivate = $this->privateProfileCheck($user, $loggedIn);
}
return $this->buildProfile($request, $user);
}
$isBlocked = $this->blockedProfileCheck($user);
protected function buildProfile(Request $request, $user)
{
$username = $user->username;
$loggedIn = Auth::check();
$isPrivate = false;
$isBlocked = false;
if (! $loggedIn) {
$key = 'profile:settings:'.$user->id;
$ttl = now()->addHours(6);
$settings = Cache::remember($key, $ttl, function () use ($user) {
return $user->user->settings;
});
$owner = $loggedIn && Auth::id() === $user->user_id;
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
if ($user->is_private == true) {
$profile = null;
if ($isPrivate == true || $isBlocked == true) {
$requested = Auth::check() ? FollowRequest::whereFollowerId(Auth::user()->profile_id)
->whereFollowingId($user->id)
->exists() : false;
return view('profile.private', compact('user', 'is_following', 'requested'));
}
return view('profile.private', compact('user'));
}
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
$profile = $user;
$settings = [
'crawlable' => $settings->crawlable,
'following' => [
'count' => $settings->show_profile_following_count,
'list' => $settings->show_profile_following
],
'followers' => [
'count' => $settings->show_profile_follower_count,
'list' => $settings->show_profile_followers
]
];
return view('profile.show', compact('profile', 'settings'));
}
}
$owner = false;
$is_following = false;
public function permalinkRedirect(Request $request, $username)
{
$user = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
$profile = $user;
$settings = [
'crawlable' => $settings->crawlable,
'following' => [
'count' => $settings->show_profile_following_count,
'list' => $settings->show_profile_following,
],
'followers' => [
'count' => $settings->show_profile_follower_count,
'list' => $settings->show_profile_followers,
],
];
if ($request->wantsJson() && config_cache('federation.activitypub.enabled')) {
return $this->showActivityPub($request, $user);
}
return view('profile.show', compact('profile', 'settings'));
} else {
$key = 'profile:settings:'.$user->id;
$ttl = now()->addHours(6);
$settings = Cache::remember($key, $ttl, function () use ($user) {
return $user->user->settings;
});
return redirect($user->url());
}
if ($user->is_private == true) {
$isPrivate = $this->privateProfileCheck($user, $loggedIn);
}
protected function privateProfileCheck(Profile $profile, $loggedIn)
{
if (!Auth::check()) {
return true;
}
$isBlocked = $this->blockedProfileCheck($user);
$user = Auth::user()->profile;
if($user->id == $profile->id || !$profile->is_private) {
return false;
}
$owner = $loggedIn && Auth::id() === $user->user_id;
$is_following = ($owner == false && Auth::check()) ? $user->followedBy(Auth::user()->profile) : false;
$follows = Follower::whereProfileId($user->id)->whereFollowingId($profile->id)->exists();
if ($follows == false) {
return true;
}
if ($isPrivate == true || $isBlocked == true) {
$requested = Auth::check() ? FollowRequest::whereFollowerId(Auth::user()->profile_id)
->whereFollowingId($user->id)
->exists() : false;
return false;
}
return view('profile.private', compact('user', 'is_following', 'requested'));
}
public static function accountCheck(Profile $profile)
{
switch ($profile->status) {
case 'disabled':
case 'suspended':
case 'delete':
return view('profile.disabled');
break;
$is_admin = is_null($user->domain) ? $user->user->is_admin : false;
$profile = $user;
$settings = [
'crawlable' => $settings->crawlable,
'following' => [
'count' => $settings->show_profile_following_count,
'list' => $settings->show_profile_following,
],
'followers' => [
'count' => $settings->show_profile_follower_count,
'list' => $settings->show_profile_followers,
],
];
default:
break;
}
return abort(404);
}
return view('profile.show', compact('profile', 'settings'));
}
}
protected function blockedProfileCheck(Profile $profile)
{
$pid = Auth::user()->profile->id;
$blocks = UserFilter::whereUserId($profile->id)
->whereFilterType('block')
->whereFilterableType('App\Profile')
->pluck('filterable_id')
->toArray();
if (in_array($pid, $blocks)) {
return true;
}
protected function getCachedUser($username, $withTrashed = false)
{
$val = str_replace(['_', '.', '-'], '', $username);
if (! ctype_alnum($val)) {
return;
}
$hash = ($withTrashed ? 'wt:' : 'wot:').strtolower($username);
return false;
}
return Cache::remember('pfc:cached-user:'.$hash, ($withTrashed ? 14400 : 900), function () use ($username, $withTrashed) {
if (! $withTrashed) {
return Profile::whereNull(['domain', 'status'])
->whereUsername($username)
->first();
} else {
return Profile::withTrashed()
->whereNull('domain')
->whereUsername($username)
->first();
}
});
}
public function showActivityPub(Request $request, $user)
{
abort_if(!config_cache('federation.activitypub.enabled'), 404);
abort_if($user->domain, 404);
public function permalinkRedirect(Request $request, $username)
{
if ($request->wantsJson() && (bool) config_cache('federation.activitypub.enabled')) {
$user = $this->getCachedUser($username, true);
return Cache::remember('pf:activitypub:user-object:by-id:' . $user->id, 3600, function() use($user) {
$fractal = new Fractal\Manager();
$resource = new Fractal\Resource\Item($user, new ProfileTransformer);
$res = $fractal->createData($resource)->toArray();
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
});
}
return $this->showActivityPub($request, $user);
}
public function showAtomFeed(Request $request, $user)
{
abort_if(!config('federation.atom.enabled'), 404);
$user = $this->getCachedUser($username);
$pid = AccountService::usernameToId($user);
abort_if(! $user, 404);
abort_if(!$pid, 404);
return redirect($user->url());
}
$profile = AccountService::get($pid, true);
protected function privateProfileCheck(Profile $profile, $loggedIn)
{
if (! Auth::check()) {
return true;
}
abort_if(!$profile || $profile['locked'] || !$profile['local'], 404);
$user = Auth::user()->profile;
if ($user->id == $profile->id || ! $profile->is_private) {
return false;
}
$data = Cache::remember('pf:atom:user-feed:by-id:' . $profile['id'], 43200, function() use($pid, $profile) {
$items = DB::table('statuses')
->whereProfileId($pid)
->whereVisibility('public')
->whereType('photo')
->orderByDesc('id')
->take(10)
->get()
->map(function($status) {
return StatusService::get($status->id);
})
->filter(function($status) {
return $status &&
isset($status['account']) &&
isset($status['media_attachments']) &&
count($status['media_attachments']);
})
->values();
$permalink = config('app.url') . "/users/{$profile['username']}.atom";
$headers = ['Content-Type' => 'application/atom+xml'];
$follows = Follower::whereProfileId($user->id)->whereFollowingId($profile->id)->exists();
if ($follows == false) {
return true;
}
if($items && $items->count()) {
$headers['Last-Modified'] = now()->parse($items->first()['created_at'])->toRfc7231String();
}
return false;
}
return compact('items', 'permalink', 'headers');
});
abort_if(!$data, 404);
return response()
->view('atom.user',
[
'profile' => $profile,
'items' => $data['items'],
'permalink' => $data['permalink']
]
)
->withHeaders($data['headers']);
}
public static function accountCheck(Profile $profile)
{
switch ($profile->status) {
case 'disabled':
case 'suspended':
case 'delete':
return view('profile.disabled');
break;
public function meRedirect()
{
abort_if(!Auth::check(), 404);
return redirect(Auth::user()->url());
}
default:
break;
}
public function embed(Request $request, $username)
{
$res = view('profile.embed-removed');
return abort(404);
}
if(!config('instance.embed.profile')) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
protected function blockedProfileCheck(Profile $profile)
{
$pid = Auth::user()->profile->id;
$blocks = UserFilter::whereUserId($profile->id)
->whereFilterType('block')
->whereFilterableType('App\Profile')
->pluck('filterable_id')
->toArray();
if (in_array($pid, $blocks)) {
return true;
}
if(strlen($username) > 15 || strlen($username) < 2) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
return false;
}
$profile = Profile::whereUsername($username)
->whereIsPrivate(false)
->whereNull('status')
->whereNull('domain')
->first();
public function showActivityPub(Request $request, $user)
{
abort_if(! config_cache('federation.activitypub.enabled'), 404);
abort_if(! $user, 404, 'Not found');
abort_if($user->domain, 404);
if(!$profile) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
return Cache::remember('pf:activitypub:user-object:by-id:'.$user->id, 1800, function () use ($user) {
$fractal = new Fractal\Manager();
$resource = new Fractal\Resource\Item($user, new ProfileTransformer);
$res = $fractal->createData($resource)->toArray();
if(AccountService::canEmbed($profile->user_id) == false) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json');
});
}
$profile = AccountService::get($profile->id);
$res = view('profile.embed', compact('profile'));
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
public function showAtomFeed(Request $request, $user)
{
abort_if(! config('federation.atom.enabled'), 404);
public function stories(Request $request, $username)
{
abort_if(!config_cache('instance.stories.enabled') || !$request->user(), 404);
$profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
$pid = $profile->id;
$authed = Auth::user()->profile_id;
abort_if($pid != $authed && !FollowerService::follows($authed, $pid), 404);
$exists = Story::whereProfileId($pid)
->whereActive(true)
->exists();
abort_unless($exists, 404);
return view('profile.story', compact('pid', 'profile'));
}
$pid = AccountService::usernameToId($user);
abort_if(! $pid, 404);
$profile = AccountService::get($pid, true);
abort_if(! $profile || $profile['locked'] || ! $profile['local'], 404);
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 3600, function () use ($profile) {
$uid = User::whereProfileId($profile['id'])->first();
if (! $uid) {
return true;
}
$exists = AccountInterstitial::whereUserId($uid->id)->where('is_spam', 1)->count();
if ($exists) {
return true;
}
return false;
});
abort_if($aiCheck, 404);
$enabled = Cache::remember('profile:atom:enabled:'.$profile['id'], 84600, function () use ($profile) {
$uid = User::whereProfileId($profile['id'])->first();
if (! $uid) {
return false;
}
$settings = UserSetting::whereUserId($uid->id)->first();
if (! $settings) {
return false;
}
return $settings->show_atom;
});
abort_if(! $enabled, 404);
$data = Cache::remember('pf:atom:user-feed:by-id:'.$profile['id'], 14400, function () use ($pid, $profile) {
$items = Status::whereProfileId($pid)
->whereScope('public')
->whereIn('type', ['photo', 'photo:album'])
->orderByDesc('id')
->take(10)
->get()
->map(function ($status) {
return StatusService::get($status->id, true);
})
->filter(function ($status) {
return $status &&
isset($status['account']) &&
isset($status['media_attachments']) &&
count($status['media_attachments']);
})
->values();
$permalink = config('app.url')."/users/{$profile['username']}.atom";
$headers = ['Content-Type' => 'application/atom+xml'];
if ($items && $items->count()) {
$headers['Last-Modified'] = now()->parse($items->first()['created_at'])->toRfc7231String();
}
return compact('items', 'permalink', 'headers');
});
abort_if(! $data || ! isset($data['items']) || ! isset($data['permalink']), 404);
return response()
->view('atom.user',
[
'profile' => $profile,
'items' => $data['items'],
'permalink' => $data['permalink'],
]
)
->withHeaders($data['headers']);
}
public function meRedirect()
{
abort_if(! Auth::check(), 404);
return redirect(Auth::user()->url());
}
public function embed(Request $request, $username)
{
$res = view('profile.embed-removed');
if (! (bool) config_cache('instance.embed.profile')) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
if (strlen($username) > 15 || strlen($username) < 2) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$profile = $this->getCachedUser($username);
if (! $profile || $profile->is_private) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile->id, 3600, function () use ($profile) {
$exists = AccountInterstitial::whereUserId($profile->user_id)->where('is_spam', 1)->count();
if ($exists) {
return true;
}
return false;
});
if ($aiCheck) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
if (AccountService::canEmbed($profile->user_id) == false) {
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
$profile = AccountService::get($profile->id);
$res = view('profile.embed', compact('profile'));
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
}
public function stories(Request $request, $username)
{
abort_if(! (bool) config_cache('instance.stories.enabled') || ! $request->user(), 404);
$profile = Profile::whereNull('domain')->whereUsername($username)->firstOrFail();
$pid = $profile->id;
$authed = Auth::user()->profile_id;
abort_if($pid != $authed && ! FollowerService::follows($authed, $pid), 404);
$exists = Story::whereProfileId($pid)
->whereActive(true)
->exists();
abort_unless($exists, 404);
return view('profile.story', compact('pid', 'profile'));
}
}

Wyświetl plik

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ProfileMigrationStoreRequest;
use App\Jobs\ProfilePipeline\ProfileMigrationDeliverMoveActivityPipeline;
use App\Jobs\ProfilePipeline\ProfileMigrationMoveFollowersPipeline;
use App\Models\ProfileAlias;
use App\Models\ProfileMigration;
use App\Services\AccountService;
use App\Services\WebfingerService;
use App\Util\ActivityPub\Helpers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
class ProfileMigrationController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index(Request $request)
{
abort_if((bool) config_cache('federation.activitypub.enabled') === false, 404);
if ((bool) config_cache('federation.migration') === false) {
return redirect(route('help.account-migration'));
}
$hasExistingMigration = ProfileMigration::whereProfileId($request->user()->profile_id)
->where('created_at', '>', now()->subDays(30))
->exists();
return view('settings.migration.index', compact('hasExistingMigration'));
}
public function store(ProfileMigrationStoreRequest $request)
{
abort_if((bool) config_cache('federation.activitypub.enabled') === false, 404);
$acct = WebfingerService::rawGet($request->safe()->acct);
if (! $acct) {
return redirect()->back()->withErrors(['acct' => 'The new account you provided is not responding to our requests.']);
}
$newAccount = Helpers::profileFetch($acct);
if (! $newAccount) {
return redirect()->back()->withErrors(['acct' => 'An error occured, please try again later. Code: res-failed-account-fetch']);
}
$user = $request->user();
ProfileAlias::updateOrCreate([
'profile_id' => $user->profile_id,
'acct' => $request->safe()->acct,
'uri' => $acct,
]);
$migration = ProfileMigration::create([
'profile_id' => $request->user()->profile_id,
'acct' => $request->safe()->acct,
'followers_count' => $request->user()->profile->followers_count,
'target_profile_id' => $newAccount['id'],
]);
$user->profile->update([
'moved_to_profile_id' => $newAccount->id,
'indexable' => false,
]);
AccountService::del($user->profile_id);
Bus::batch([
new ProfileMigrationDeliverMoveActivityPipeline($migration, $user->profile, $newAccount),
new ProfileMigrationMoveFollowersPipeline($user->profile_id, $newAccount->id),
])->onQueue('follow')->dispatch();
return redirect()->back()->with(['status' => 'Succesfully migrated account!']);
}
}

Wyświetl plik

@ -42,6 +42,7 @@ use App\Services\{
use App\Jobs\StatusPipeline\NewStatusPipeline;
use League\Fractal\Serializer\ArraySerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use App\Services\InstanceService;
class PublicApiController extends Controller
{
@ -661,6 +662,10 @@ class PublicApiController extends Controller
public function account(Request $request, $id)
{
$res = AccountService::get($id);
if($res && isset($res['local'], $res['url']) && !$res['local']) {
$domain = parse_url($res['url'], PHP_URL_HOST);
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
}
return response()->json($res);
}
@ -680,6 +685,11 @@ class PublicApiController extends Controller
$profile = AccountService::get($id);
abort_if(!$profile, 404);
if($profile && isset($profile['local'], $profile['url']) && !$profile['local']) {
$domain = parse_url($profile['url'], PHP_URL_HOST);
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
}
$limit = $request->limit ?? 9;
$max_id = $request->max_id;
$min_id = $request->min_id;

Wyświetl plik

@ -0,0 +1,728 @@
<?php
namespace App\Http\Controllers;
use App\Models\RemoteAuth;
use App\Services\Account\RemoteAuthService;
use App\Services\EmailService;
use App\Services\MediaStorageService;
use App\User;
use App\Util\ActivityPub\Helpers;
use App\Util\Lexer\RestrictedNames;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Purify;
class RemoteAuthController extends Controller
{
public function start(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
if ($request->user()) {
return redirect('/');
}
return view('auth.remote.start');
}
public function startRedirect(Request $request)
{
return redirect('/login');
}
public function getAuthDomains(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
if (config('remote-auth.mastodon.domains.only_custom')) {
$res = config('remote-auth.mastodon.domains.custom');
if (! $res || ! strlen($res)) {
return [];
}
$res = explode(',', $res);
return response()->json($res);
}
if (config('remote-auth.mastodon.domains.custom') &&
! config('remote-auth.mastodon.domains.only_default') &&
strlen(config('remote-auth.mastodon.domains.custom')) > 3 &&
strpos(config('remote-auth.mastodon.domains.custom'), '.') > -1
) {
$res = config('remote-auth.mastodon.domains.custom');
if (! $res || ! strlen($res)) {
return [];
}
$res = explode(',', $res);
return response()->json($res);
}
$res = config('remote-auth.mastodon.domains.default');
$res = explode(',', $res);
return response()->json($res);
}
public function redirect(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
$this->validate($request, ['domain' => 'required']);
$domain = $request->input('domain');
if (str_starts_with(strtolower($domain), 'http')) {
$res = [
'domain' => $domain,
'ready' => false,
'action' => 'incompatible_domain',
];
return response()->json($res);
}
$validateInstance = Helpers::validateUrl('https://'.$domain.'/?block-check='.time());
if (! $validateInstance) {
$res = [
'domain' => $domain,
'ready' => false,
'action' => 'blocked_domain',
];
return response()->json($res);
}
$compatible = RemoteAuthService::isDomainCompatible($domain);
if (! $compatible) {
$res = [
'domain' => $domain,
'ready' => false,
'action' => 'incompatible_domain',
];
return response()->json($res);
}
if (config('remote-auth.mastodon.domains.only_default')) {
$defaultDomains = explode(',', config('remote-auth.mastodon.domains.default'));
if (! in_array($domain, $defaultDomains)) {
$res = [
'domain' => $domain,
'ready' => false,
'action' => 'incompatible_domain',
];
return response()->json($res);
}
}
if (config('remote-auth.mastodon.domains.only_custom') && config('remote-auth.mastodon.domains.custom')) {
$customDomains = explode(',', config('remote-auth.mastodon.domains.custom'));
if (! in_array($domain, $customDomains)) {
$res = [
'domain' => $domain,
'ready' => false,
'action' => 'incompatible_domain',
];
return response()->json($res);
}
}
$client = RemoteAuthService::getMastodonClient($domain);
abort_unless($client, 422, 'Invalid mastodon client');
$request->session()->put('state', $state = Str::random(40));
$request->session()->put('oauth_domain', $domain);
$query = http_build_query([
'client_id' => $client->client_id,
'redirect_uri' => $client->redirect_uri,
'response_type' => 'code',
'scope' => 'read',
'state' => $state,
]);
$request->session()->put('oauth_redirect_to', 'https://'.$domain.'/oauth/authorize?'.$query);
$dsh = Str::random(17);
$res = [
'domain' => $domain,
'ready' => true,
'dsh' => $dsh,
];
return response()->json($res);
}
public function preflight(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
if (! $request->filled('d') || ! $request->filled('dsh') || ! $request->session()->exists('oauth_redirect_to')) {
return redirect('/login');
}
return redirect()->away($request->session()->pull('oauth_redirect_to'));
}
public function handleCallback(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
$domain = $request->session()->get('oauth_domain');
if ($request->filled('code')) {
$code = $request->input('code');
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class,
'Invalid state value.'
);
$res = RemoteAuthService::getToken($domain, $code);
if (! $res || ! isset($res['access_token'])) {
$request->session()->regenerate();
return redirect('/login');
}
$request->session()->put('oauth_remote_session_token', $res['access_token']);
return redirect('/auth/mastodon/getting-started');
}
return redirect('/login');
}
public function onboarding(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
if ($request->user()) {
return redirect('/');
}
return view('auth.remote.onboarding');
}
public function sessionCheck(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 403);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
$domain = $request->session()->get('oauth_domain');
$token = $request->session()->get('oauth_remote_session_token');
$res = RemoteAuthService::getVerifyCredentials($domain, $token);
abort_if(! $res || ! isset($res['acct']), 403, 'Invalid credentials');
$webfinger = strtolower('@'.$res['acct'].'@'.$domain);
$request->session()->put('oauth_masto_webfinger', $webfinger);
if (config('remote-auth.mastodon.max_uses.enabled')) {
$limit = config('remote-auth.mastodon.max_uses.limit');
$uses = RemoteAuthService::lookupWebfingerUses($webfinger);
if ($uses >= $limit) {
return response()->json([
'code' => 200,
'msg' => 'Success!',
'action' => 'max_uses_reached',
]);
}
}
$exists = RemoteAuth::whereDomain($domain)->where('webfinger', $webfinger)->whereNotNull('user_id')->first();
if ($exists && $exists->user_id) {
return response()->json([
'code' => 200,
'msg' => 'Success!',
'action' => 'redirect_existing_user',
]);
}
return response()->json([
'code' => 200,
'msg' => 'Success!',
'action' => 'onboard',
]);
}
public function sessionGetMastodonData(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 403);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
$domain = $request->session()->get('oauth_domain');
$token = $request->session()->get('oauth_remote_session_token');
$res = RemoteAuthService::getVerifyCredentials($domain, $token);
$res['_webfinger'] = strtolower('@'.$res['acct'].'@'.$domain);
$res['_domain'] = strtolower($domain);
$request->session()->put('oauth_remasto_id', $res['id']);
$ra = RemoteAuth::updateOrCreate([
'domain' => $domain,
'webfinger' => $res['_webfinger'],
], [
'software' => 'mastodon',
'ip_address' => $request->ip(),
'bearer_token' => $token,
'verify_credentials' => $res,
'last_verify_credentials_at' => now(),
'last_successful_login_at' => now(),
]);
$request->session()->put('oauth_masto_raid', $ra->id);
return response()->json($res);
}
public function sessionValidateUsername(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 403);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
$this->validate($request, [
'username' => [
'required',
'min:2',
'max:15',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
$underscore = substr_count($value, '_');
$period = substr_count($value, '.');
if (ends_with($value, ['.php', '.js', '.css'])) {
return $fail('Username is invalid.');
}
if (($dash + $underscore + $period) > 1) {
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
}
if (! ctype_alnum($value[0])) {
return $fail('Username is invalid. Must start with a letter or number.');
}
if (! ctype_alnum($value[strlen($value) - 1])) {
return $fail('Username is invalid. Must end with a letter or number.');
}
$val = str_replace(['_', '.', '-'], '', $value);
if (! ctype_alnum($val)) {
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
}
$restricted = RestrictedNames::get();
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
return $fail('Username cannot be used.');
}
},
],
]);
$username = strtolower($request->input('username'));
$exists = User::where('username', $username)->exists();
return response()->json([
'code' => 200,
'username' => $username,
'exists' => $exists,
]);
}
public function sessionValidateEmail(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 403);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
$this->validate($request, [
'email' => [
'required',
'email:strict,filter_unicode,dns,spoof',
],
]);
$email = $request->input('email');
$banned = EmailService::isBanned($email);
$exists = User::where('email', $email)->exists();
return response()->json([
'code' => 200,
'email' => $email,
'exists' => $exists,
'banned' => $banned,
]);
}
public function sessionGetMastodonFollowers(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
abort_unless($request->session()->exists('oauth_remasto_id'), 403);
$domain = $request->session()->get('oauth_domain');
$token = $request->session()->get('oauth_remote_session_token');
$id = $request->session()->get('oauth_remasto_id');
$res = RemoteAuthService::getFollowing($domain, $token, $id);
if (! $res) {
return response()->json([
'code' => 200,
'following' => [],
]);
}
$res = collect($res)->filter(fn ($acct) => Helpers::validateUrl($acct['url']))->values()->toArray();
return response()->json([
'code' => 200,
'following' => $res,
]);
}
public function handleSubmit(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
abort_unless($request->session()->exists('oauth_remasto_id'), 403);
abort_unless($request->session()->exists('oauth_masto_webfinger'), 403);
abort_unless($request->session()->exists('oauth_masto_raid'), 403);
$this->validate($request, [
'email' => 'required|email:strict,filter_unicode,dns,spoof',
'username' => [
'required',
'min:2',
'max:15',
'unique:users,username',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
$underscore = substr_count($value, '_');
$period = substr_count($value, '.');
if (ends_with($value, ['.php', '.js', '.css'])) {
return $fail('Username is invalid.');
}
if (($dash + $underscore + $period) > 1) {
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
}
if (! ctype_alnum($value[0])) {
return $fail('Username is invalid. Must start with a letter or number.');
}
if (! ctype_alnum($value[strlen($value) - 1])) {
return $fail('Username is invalid. Must end with a letter or number.');
}
$val = str_replace(['_', '.', '-'], '', $value);
if (! ctype_alnum($val)) {
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
}
$restricted = RestrictedNames::get();
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
return $fail('Username cannot be used.');
}
},
],
'password' => 'required|string|min:8|confirmed',
'name' => 'nullable|max:30',
]);
$email = $request->input('email');
$username = $request->input('username');
$password = $request->input('password');
$name = $request->input('name');
$user = $this->createUser([
'name' => $name,
'username' => $username,
'password' => $password,
'email' => $email,
]);
$raid = $request->session()->pull('oauth_masto_raid');
$webfinger = $request->session()->pull('oauth_masto_webfinger');
$token = $user->createToken('Onboarding')->accessToken;
$ra = RemoteAuth::where('id', $raid)->where('webfinger', $webfinger)->firstOrFail();
$ra->user_id = $user->id;
$ra->save();
return [
'code' => 200,
'msg' => 'Success',
'token' => $token,
];
}
public function storeBio(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_unless($request->user(), 404);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
abort_unless($request->session()->exists('oauth_remasto_id'), 403);
$this->validate($request, [
'bio' => 'required|nullable|max:500',
]);
$profile = $request->user()->profile;
$profile->bio = Purify::clean($request->input('bio'));
$profile->save();
return [200];
}
public function accountToId(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 404);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
abort_unless($request->session()->exists('oauth_remasto_id'), 403);
$this->validate($request, [
'account' => 'required|url',
]);
$account = $request->input('account');
abort_unless(substr(strtolower($account), 0, 8) === 'https://', 404);
$host = strtolower(config('pixelfed.domain.app'));
$domain = strtolower(parse_url($account, PHP_URL_HOST));
if ($domain == $host) {
$username = Str::of($account)->explode('/')->last();
$user = User::where('username', $username)->first();
if ($user) {
return ['id' => (string) $user->profile_id];
} else {
return [];
}
} else {
try {
$profile = Helpers::profileFetch($account);
if ($profile) {
return ['id' => (string) $profile->id];
} else {
return [];
}
} catch (\GuzzleHttp\Exception\RequestException $e) {
return;
} catch (Exception $e) {
return [];
}
}
}
public function storeAvatar(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_unless($request->user(), 404);
$this->validate($request, [
'avatar_url' => 'required|active_url',
]);
$user = $request->user();
$profile = $user->profile;
abort_if(! $profile->avatar, 404, 'Missing avatar');
$avatar = $profile->avatar;
$avatar->remote_url = $request->input('avatar_url');
$avatar->save();
MediaStorageService::avatar($avatar, (bool) config_cache('pixelfed.cloud_storage') == false);
return [200];
}
public function finishUp(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_unless($request->user(), 404);
$currentWebfinger = '@'.$request->user()->username.'@'.config('pixelfed.domain.app');
$ra = RemoteAuth::where('user_id', $request->user()->id)->firstOrFail();
RemoteAuthService::submitToBeagle(
$ra->webfinger,
$ra->verify_credentials['url'],
$currentWebfinger,
$request->user()->url()
);
return [200];
}
public function handleLogin(Request $request)
{
abort_unless((
config_cache('pixelfed.open_registration') &&
config('remote-auth.mastodon.enabled')
) || (
config('remote-auth.mastodon.ignore_closed_state') &&
config('remote-auth.mastodon.enabled')
), 404);
abort_if($request->user(), 404);
abort_unless($request->session()->exists('oauth_domain'), 403);
abort_unless($request->session()->exists('oauth_remote_session_token'), 403);
abort_unless($request->session()->exists('oauth_masto_webfinger'), 403);
$domain = $request->session()->get('oauth_domain');
$wf = $request->session()->get('oauth_masto_webfinger');
$ra = RemoteAuth::where('webfinger', $wf)->where('domain', $domain)->whereNotNull('user_id')->firstOrFail();
$user = User::findOrFail($ra->user_id);
abort_if($user->is_admin || $user->status != null, 422, 'Invalid auth action');
Auth::loginUsingId($ra->user_id);
return [200];
}
protected function createUser($data)
{
event(new Registered($user = User::create([
'name' => Purify::clean($data['name']),
'username' => $data['username'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'email_verified_at' => config('remote-auth.mastodon.contraints.skip_email_verification') ? now() : null,
'app_register_ip' => request()->ip(),
'register_source' => 'mastodon',
])));
$this->guarder()->login($user);
return $user;
}
protected function guarder()
{
return Auth::guard();
}
}

Wyświetl plik

@ -2,368 +2,367 @@
namespace App\Http\Controllers;
use Auth;
use App\Hashtag;
use App\Place;
use App\Profile;
use App\Services\WebfingerService;
use App\Status;
use Illuminate\Http\Request;
use App\Util\ActivityPub\Helpers;
use Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use App\Transformer\Api\{
AccountTransformer,
HashtagTransformer,
StatusTransformer,
};
use App\Services\WebfingerService;
class SearchController extends Controller
{
public $tokens = [];
public $term = '';
public $hash = '';
public $cacheKey = 'api:search:tag:';
public $tokens = [];
public function __construct()
{
$this->middleware('auth');
}
public $term = '';
public function searchAPI(Request $request)
{
$this->validate($request, [
'q' => 'required|string|min:3|max:120',
'src' => 'required|string|in:metro',
'v' => 'required|integer|in:2',
'scope' => 'required|in:all,hashtag,profile,remote,webfinger'
]);
public $hash = '';
$scope = $request->input('scope') ?? 'all';
$this->term = e(urldecode($request->input('q')));
$this->hash = hash('sha256', $this->term);
public $cacheKey = 'api:search:tag:';
switch ($scope) {
case 'all':
$this->getHashtags();
$this->getPosts();
$this->getProfiles();
// $this->getPlaces();
break;
public function __construct()
{
$this->middleware('auth');
}
case 'hashtag':
$this->getHashtags();
break;
public function searchAPI(Request $request)
{
$this->validate($request, [
'q' => 'required|string|min:3|max:120',
'src' => 'required|string|in:metro',
'v' => 'required|integer|in:2',
'scope' => 'required|in:all,hashtag,profile,remote,webfinger',
]);
case 'profile':
$this->getProfiles();
break;
$scope = $request->input('scope') ?? 'all';
$this->term = e(urldecode($request->input('q')));
$this->hash = hash('sha256', $this->term);
case 'webfinger':
$this->webfingerSearch();
break;
switch ($scope) {
case 'all':
$this->getHashtags();
$this->getPosts();
$this->getProfiles();
// $this->getPlaces();
break;
case 'remote':
$this->remoteLookupSearch();
break;
case 'hashtag':
$this->getHashtags();
break;
case 'place':
$this->getPlaces();
break;
case 'profile':
$this->getProfiles();
break;
default:
break;
}
case 'webfinger':
$this->webfingerSearch();
break;
return response()->json($this->tokens, 200, [], JSON_PRETTY_PRINT);
}
case 'remote':
$this->remoteLookupSearch();
break;
protected function getPosts()
{
$tag = $this->term;
$hash = hash('sha256', $tag);
if( Helpers::validateUrl($tag) != false &&
Helpers::validateLocalUrl($tag) != true &&
config_cache('federation.activitypub.enabled') == true &&
config('federation.activitypub.remoteFollow') == true
) {
$remote = Helpers::fetchFromUrl($tag);
if( isset($remote['type']) &&
$remote['type'] == 'Note') {
$item = Helpers::statusFetch($tag);
$this->tokens['posts'] = [[
'count' => 0,
'url' => $item->url(),
'type' => 'status',
'value' => "by {$item->profile->username} <span class='float-right'>{$item->created_at->diffForHumans(null, true, true)}</span>",
'tokens' => [$item->caption],
'name' => $item->caption,
'thumb' => $item->thumb(),
]];
}
} else {
$posts = Status::select('id', 'profile_id', 'caption', 'created_at')
->whereHas('media')
->whereNull('in_reply_to_id')
->whereNull('reblog_of_id')
->whereProfileId(Auth::user()->profile_id)
->where('caption', 'like', '%'.$tag.'%')
->latest()
->limit(10)
->get();
case 'place':
$this->getPlaces();
break;
if($posts->count() > 0) {
$posts = $posts->map(function($item, $key) {
return [
'count' => 0,
'url' => $item->url(),
'type' => 'status',
'value' => "by {$item->profile->username} <span class='float-right'>{$item->created_at->diffForHumans(null, true, true)}</span>",
'tokens' => [$item->caption],
'name' => $item->caption,
'thumb' => $item->thumb(),
'filter' => $item->firstMedia()->filter_class
];
});
$this->tokens['posts'] = $posts;
}
}
}
default:
break;
}
protected function getHashtags()
{
$tag = $this->term;
$key = $this->cacheKey . 'hashtags:' . $this->hash;
$ttl = now()->addMinutes(1);
$tokens = Cache::remember($key, $ttl, function() use($tag) {
$htag = Str::startsWith($tag, '#') == true ? mb_substr($tag, 1) : $tag;
$hashtags = Hashtag::select('id', 'name', 'slug')
->where('slug', 'like', '%'.$htag.'%')
->whereHas('posts')
->limit(20)
->get();
if($hashtags->count() > 0) {
$tags = $hashtags->map(function ($item, $key) {
return [
'count' => $item->posts()->count(),
'url' => $item->url(),
'type' => 'hashtag',
'value' => $item->name,
'tokens' => '',
'name' => null,
];
});
return $tags;
}
});
$this->tokens['hashtags'] = $tokens;
}
return response()->json($this->tokens, 200, [], JSON_PRETTY_PRINT);
}
protected function getPlaces()
{
$tag = $this->term;
// $key = $this->cacheKey . 'places:' . $this->hash;
// $ttl = now()->addHours(12);
// $tokens = Cache::remember($key, $ttl, function() use($tag) {
$htag = Str::contains($tag, ',') == true ? explode(',', $tag) : [$tag];
$hashtags = Place::select('id', 'name', 'slug', 'country')
->where('name', 'like', '%'.$htag[0].'%')
->paginate(20);
$tags = [];
if($hashtags->count() > 0) {
$tags = $hashtags->map(function ($item, $key) {
return [
'count' => null,
'url' => $item->url(),
'type' => 'place',
'value' => $item->name . ', ' . $item->country,
'tokens' => '',
'name' => null,
'city' => $item->name,
'country' => $item->country
];
});
// return $tags;
}
// });
$this->tokens['places'] = $tags;
$this->tokens['placesPagination'] = [
'total' => $hashtags->total(),
'current_page' => $hashtags->currentPage(),
'last_page' => $hashtags->lastPage()
];
}
protected function getPosts()
{
$tag = $this->term;
$hash = hash('sha256', $tag);
if (Helpers::validateUrl($tag) != false &&
Helpers::validateLocalUrl($tag) != true &&
(bool) config_cache('federation.activitypub.enabled') == true &&
config('federation.activitypub.remoteFollow') == true
) {
$remote = Helpers::fetchFromUrl($tag);
if (isset($remote['type']) &&
in_array($remote['type'], ['Note', 'Question'])
) {
$item = Helpers::statusFetch($tag);
$this->tokens['posts'] = [[
'count' => 0,
'url' => $item->url(),
'type' => 'status',
'value' => "by {$item->profile->username} <span class='float-right'>{$item->created_at->diffForHumans(null, true, true)}</span>",
'tokens' => [$item->caption],
'name' => $item->caption,
'thumb' => $item->thumb(),
]];
}
} else {
$posts = Status::select('id', 'profile_id', 'caption', 'created_at')
->whereHas('media')
->whereNull('in_reply_to_id')
->whereNull('reblog_of_id')
->whereProfileId(Auth::user()->profile_id)
->where('caption', 'like', '%'.$tag.'%')
->latest()
->limit(10)
->get();
protected function getProfiles()
{
$tag = $this->term;
$remoteKey = $this->cacheKey . 'profiles:remote:' . $this->hash;
$key = $this->cacheKey . 'profiles:' . $this->hash;
$remoteTtl = now()->addMinutes(15);
$ttl = now()->addHours(2);
if( Helpers::validateUrl($tag) != false &&
Helpers::validateLocalUrl($tag) != true &&
config_cache('federation.activitypub.enabled') == true &&
config('federation.activitypub.remoteFollow') == true
) {
$remote = Helpers::fetchFromUrl($tag);
if( isset($remote['type']) &&
$remote['type'] == 'Person'
) {
$this->tokens['profiles'] = Cache::remember($remoteKey, $remoteTtl, function() use($tag) {
$item = Helpers::profileFirstOrNew($tag);
$tokens = [[
'count' => 1,
'url' => $item->url(),
'type' => 'profile',
'value' => $item->username,
'tokens' => [$item->username],
'name' => $item->name,
'entity' => [
'id' => (string) $item->id,
'following' => $item->followedBy(Auth::user()->profile),
'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id),
'thumb' => $item->avatarUrl(),
'local' => (bool) !$item->domain,
'post_count' => $item->statuses()->count()
]
]];
return $tokens;
});
}
}
if ($posts->count() > 0) {
$posts = $posts->map(function ($item, $key) {
return [
'count' => 0,
'url' => $item->url(),
'type' => 'status',
'value' => "by {$item->profile->username} <span class='float-right'>{$item->created_at->diffForHumans(null, true, true)}</span>",
'tokens' => [$item->caption],
'name' => $item->caption,
'thumb' => $item->thumb(),
'filter' => $item->firstMedia()->filter_class,
];
});
$this->tokens['posts'] = $posts;
}
}
}
else {
$this->tokens['profiles'] = Cache::remember($key, $ttl, function() use($tag) {
if(Str::startsWith($tag, '@')) {
$tag = substr($tag, 1);
}
$users = Profile::select('status', 'domain', 'username', 'name', 'id')
->whereNull('status')
->where('username', 'like', '%'.$tag.'%')
->limit(20)
->orderBy('domain')
->get();
protected function getHashtags()
{
$tag = $this->term;
$key = $this->cacheKey.'hashtags:'.$this->hash;
$ttl = now()->addMinutes(1);
$tokens = Cache::remember($key, $ttl, function () use ($tag) {
$htag = Str::startsWith($tag, '#') == true ? mb_substr($tag, 1) : $tag;
$hashtags = Hashtag::select('id', 'name', 'slug')
->where('slug', 'like', '%'.$htag.'%')
->whereHas('posts')
->limit(20)
->get();
if ($hashtags->count() > 0) {
$tags = $hashtags->map(function ($item, $key) {
return [
'count' => $item->posts()->count(),
'url' => $item->url(),
'type' => 'hashtag',
'value' => $item->name,
'tokens' => '',
'name' => null,
];
});
if($users->count() > 0) {
return $users->map(function ($item, $key) {
return [
'count' => 0,
'url' => $item->url(),
'type' => 'profile',
'value' => $item->username,
'tokens' => [$item->username],
'name' => $item->name,
'avatar' => $item->avatarUrl(),
'id' => (string) $item->id,
'entity' => [
'id' => (string) $item->id,
'following' => $item->followedBy(Auth::user()->profile),
'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id),
'thumb' => $item->avatarUrl(),
'local' => (bool) !$item->domain,
'post_count' => $item->statuses()->count()
]
];
});
}
});
}
}
return $tags;
}
});
$this->tokens['hashtags'] = $tokens;
}
public function results(Request $request)
{
$this->validate($request, [
'q' => 'required|string|min:1',
]);
protected function getPlaces()
{
$tag = $this->term;
// $key = $this->cacheKey . 'places:' . $this->hash;
// $ttl = now()->addHours(12);
// $tokens = Cache::remember($key, $ttl, function() use($tag) {
$htag = Str::contains($tag, ',') == true ? explode(',', $tag) : [$tag];
$hashtags = Place::select('id', 'name', 'slug', 'country')
->where('name', 'like', '%'.$htag[0].'%')
->paginate(20);
$tags = [];
if ($hashtags->count() > 0) {
$tags = $hashtags->map(function ($item, $key) {
return [
'count' => null,
'url' => $item->url(),
'type' => 'place',
'value' => $item->name.', '.$item->country,
'tokens' => '',
'name' => null,
'city' => $item->name,
'country' => $item->country,
];
});
// return $tags;
}
// });
$this->tokens['places'] = $tags;
$this->tokens['placesPagination'] = [
'total' => $hashtags->total(),
'current_page' => $hashtags->currentPage(),
'last_page' => $hashtags->lastPage(),
];
}
return view('search.results');
}
protected function getProfiles()
{
$tag = $this->term;
$remoteKey = $this->cacheKey.'profiles:remote:'.$this->hash;
$key = $this->cacheKey.'profiles:'.$this->hash;
$remoteTtl = now()->addMinutes(15);
$ttl = now()->addHours(2);
if (Helpers::validateUrl($tag) != false &&
Helpers::validateLocalUrl($tag) != true &&
(bool) config_cache('federation.activitypub.enabled') == true &&
config('federation.activitypub.remoteFollow') == true
) {
$remote = Helpers::fetchFromUrl($tag);
if (isset($remote['type']) &&
$remote['type'] == 'Person'
) {
$this->tokens['profiles'] = Cache::remember($remoteKey, $remoteTtl, function () use ($tag) {
$item = Helpers::profileFirstOrNew($tag);
$tokens = [[
'count' => 1,
'url' => $item->url(),
'type' => 'profile',
'value' => $item->username,
'tokens' => [$item->username],
'name' => $item->name,
'entity' => [
'id' => (string) $item->id,
'following' => $item->followedBy(Auth::user()->profile),
'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id),
'thumb' => $item->avatarUrl(),
'local' => (bool) ! $item->domain,
'post_count' => $item->statuses()->count(),
],
]];
protected function webfingerSearch()
{
$wfs = WebfingerService::lookup($this->term);
return $tokens;
});
}
} else {
$this->tokens['profiles'] = Cache::remember($key, $ttl, function () use ($tag) {
if (Str::startsWith($tag, '@')) {
$tag = substr($tag, 1);
}
$users = Profile::select('status', 'domain', 'username', 'name', 'id')
->whereNull('status')
->where('username', 'like', '%'.$tag.'%')
->limit(20)
->orderBy('domain')
->get();
if(empty($wfs)) {
return;
}
if ($users->count() > 0) {
return $users->map(function ($item, $key) {
return [
'count' => 0,
'url' => $item->url(),
'type' => 'profile',
'value' => $item->username,
'tokens' => [$item->username],
'name' => $item->name,
'avatar' => $item->avatarUrl(),
'id' => (string) $item->id,
'entity' => [
'id' => (string) $item->id,
'following' => $item->followedBy(Auth::user()->profile),
'follow_request' => $item->hasFollowRequestById(Auth::user()->profile_id),
'thumb' => $item->avatarUrl(),
'local' => (bool) ! $item->domain,
'post_count' => $item->statuses()->count(),
],
];
});
}
});
}
}
$this->tokens['profiles'] = [
[
'count' => 1,
'url' => $wfs['url'],
'type' => 'profile',
'value' => $wfs['username'],
'tokens' => [$wfs['username']],
'name' => $wfs['display_name'],
'entity' => [
'id' => (string) $wfs['id'],
'following' => null,
'follow_request' => null,
'thumb' => $wfs['avatar'],
'local' => (bool) $wfs['local']
]
]
];
return;
}
public function results(Request $request)
{
$this->validate($request, [
'q' => 'required|string|min:1',
]);
protected function remotePostLookup()
{
$tag = $this->term;
$hash = hash('sha256', $tag);
$local = Helpers::validateLocalUrl($tag);
$valid = Helpers::validateUrl($tag);
return view('search.results');
}
if($valid == false || $local == true) {
return;
}
protected function webfingerSearch()
{
$wfs = WebfingerService::lookup($this->term);
if(Status::whereUri($tag)->whereLocal(false)->exists()) {
$item = Status::whereUri($tag)->first();
$media = $item->firstMedia();
$url = null;
if($media) {
$url = $media->remote_url;
}
$this->tokens['posts'] = [[
'count' => 0,
'url' => "/i/web/post/_/$item->profile_id/$item->id",
'type' => 'status',
'username' => $item->profile->username,
'caption' => $item->rendered ?? $item->caption,
'thumb' => $url,
'timestamp' => $item->created_at->diffForHumans()
]];
}
if (empty($wfs)) {
return;
}
$remote = Helpers::fetchFromUrl($tag);
$this->tokens['profiles'] = [
[
'count' => 1,
'url' => $wfs['url'],
'type' => 'profile',
'value' => $wfs['username'],
'tokens' => [$wfs['username']],
'name' => $wfs['display_name'],
'entity' => [
'id' => (string) $wfs['id'],
'following' => null,
'follow_request' => null,
'thumb' => $wfs['avatar'],
'local' => (bool) $wfs['local'],
],
],
];
if(isset($remote['type']) && $remote['type'] == 'Note') {
$item = Helpers::statusFetch($tag);
$media = $item->firstMedia();
$url = null;
if($media) {
$url = $media->remote_url;
}
$this->tokens['posts'] = [[
'count' => 0,
'url' => "/i/web/post/_/$item->profile_id/$item->id",
'type' => 'status',
'username' => $item->profile->username,
'caption' => $item->rendered ?? $item->caption,
'thumb' => $url,
'timestamp' => $item->created_at->diffForHumans()
]];
}
}
}
protected function remoteLookupSearch()
{
if(!Helpers::validateUrl($this->term)) {
return;
}
$this->getProfiles();
$this->remotePostLookup();
}
protected function remotePostLookup()
{
$tag = $this->term;
$hash = hash('sha256', $tag);
$local = Helpers::validateLocalUrl($tag);
$valid = Helpers::validateUrl($tag);
if ($valid == false || $local == true) {
return;
}
if (Status::whereUri($tag)->whereLocal(false)->exists()) {
$item = Status::whereUri($tag)->first();
$media = $item->firstMedia();
$url = null;
if ($media) {
$url = $media->remote_url;
}
$this->tokens['posts'] = [[
'count' => 0,
'url' => "/i/web/post/_/$item->profile_id/$item->id",
'type' => 'status',
'username' => $item->profile->username,
'caption' => $item->rendered ?? $item->caption,
'thumb' => $url,
'timestamp' => $item->created_at->diffForHumans(),
]];
}
$remote = Helpers::fetchFromUrl($tag);
if (isset($remote['type']) && $remote['type'] == 'Note') {
$item = Helpers::statusFetch($tag);
$media = $item->firstMedia();
$url = null;
if ($media) {
$url = $media->remote_url;
}
$this->tokens['posts'] = [[
'count' => 0,
'url' => "/i/web/post/_/$item->profile_id/$item->id",
'type' => 'status',
'username' => $item->profile->username,
'caption' => $item->rendered ?? $item->caption,
'thumb' => $url,
'timestamp' => $item->created_at->diffForHumans(),
]];
}
}
protected function remoteLookupSearch()
{
if (! Helpers::validateUrl($this->term)) {
return;
}
$this->getProfiles();
$this->remotePostLookup();
}
}

Wyświetl plik

@ -22,189 +22,189 @@ use App\Services\PronounService;
trait HomeSettings
{
public function home()
{
$id = Auth::user()->profile->id;
$storage = [];
$used = Media::whereProfileId($id)->sum('size');
$storage['limit'] = config_cache('pixelfed.max_account_size') * 1024;
$storage['used'] = $used;
$storage['percentUsed'] = ceil($storage['used'] / $storage['limit'] * 100);
$storage['limitPretty'] = PrettyNumber::size($storage['limit']);
$storage['usedPretty'] = PrettyNumber::size($storage['used']);
$pronouns = PronounService::get($id);
public function home()
{
$id = Auth::user()->profile->id;
$storage = [];
$used = Media::whereProfileId($id)->sum('size');
$storage['limit'] = config_cache('pixelfed.max_account_size') * 1024;
$storage['used'] = $used;
$storage['percentUsed'] = ceil($storage['used'] / $storage['limit'] * 100);
$storage['limitPretty'] = PrettyNumber::size($storage['limit']);
$storage['usedPretty'] = PrettyNumber::size($storage['used']);
$pronouns = PronounService::get($id);
return view('settings.home', compact('storage', 'pronouns'));
}
return view('settings.home', compact('storage', 'pronouns'));
}
public function homeUpdate(Request $request)
{
$this->validate($request, [
'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'),
'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'),
'website' => 'nullable|url',
'language' => 'nullable|string|min:2|max:5',
'pronouns' => 'nullable|array|max:4'
]);
public function homeUpdate(Request $request)
{
$this->validate($request, [
'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'),
'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'),
'website' => 'nullable|url',
'language' => 'nullable|string|min:2|max:5',
'pronouns' => 'nullable|array|max:4'
]);
$changes = false;
$name = strip_tags(Purify::clean($request->input('name')));
$bio = $request->filled('bio') ? strip_tags(Purify::clean($request->input('bio'))) : null;
$website = $request->input('website');
$language = $request->input('language');
$user = Auth::user();
$profile = $user->profile;
$pronouns = $request->input('pronouns');
$existingPronouns = PronounService::get($profile->id);
$layout = $request->input('profile_layout');
if($layout) {
$layout = !in_array($layout, ['metro', 'moment']) ? 'metro' : $layout;
}
$changes = false;
$name = strip_tags(Purify::clean($request->input('name')));
$bio = $request->filled('bio') ? strip_tags(Purify::clean($request->input('bio'))) : null;
$website = $request->input('website');
$language = $request->input('language');
$user = Auth::user();
$profile = $user->profile;
$pronouns = $request->input('pronouns');
$existingPronouns = PronounService::get($profile->id);
$layout = $request->input('profile_layout');
if($layout) {
$layout = !in_array($layout, ['metro', 'moment']) ? 'metro' : $layout;
}
$enforceEmailVerification = config_cache('pixelfed.enforce_email_verification');
$enforceEmailVerification = config_cache('pixelfed.enforce_email_verification');
// Only allow email to be updated if not yet verified
if (!$enforceEmailVerification || !$changes && $user->email_verified_at) {
if ($profile->name != $name) {
$changes = true;
$user->name = $name;
$profile->name = $name;
}
// Only allow email to be updated if not yet verified
if (!$enforceEmailVerification || !$changes && $user->email_verified_at) {
if ($profile->name != $name) {
$changes = true;
$user->name = $name;
$profile->name = $name;
}
if ($profile->website != $website) {
$changes = true;
$profile->website = $website;
}
if ($profile->website != $website) {
$changes = true;
$profile->website = $website;
}
if (strip_tags($profile->bio) != $bio) {
$changes = true;
$profile->bio = Autolink::create()->autolink($bio);
}
if (strip_tags($profile->bio) != $bio) {
$changes = true;
$profile->bio = Autolink::create()->autolink($bio);
}
if($user->language != $language &&
in_array($language, \App\Util\Localization\Localization::languages())
) {
$changes = true;
$user->language = $language;
session()->put('locale', $language);
}
if($user->language != $language &&
in_array($language, \App\Util\Localization\Localization::languages())
) {
$changes = true;
$user->language = $language;
session()->put('locale', $language);
}
if($existingPronouns != $pronouns) {
if($pronouns && in_array('Select Pronoun(s)', $pronouns)) {
PronounService::clear($profile->id);
} else {
PronounService::put($profile->id, $pronouns);
}
}
}
if($existingPronouns != $pronouns) {
if($pronouns && in_array('Select Pronoun(s)', $pronouns)) {
PronounService::clear($profile->id);
} else {
PronounService::put($profile->id, $pronouns);
}
}
}
if ($changes === true) {
$user->save();
$profile->save();
Cache::forget('user:account:id:'.$user->id);
AccountService::del($profile->id);
return redirect('/settings/home')->with('status', 'Profile successfully updated!');
}
if ($changes === true) {
$user->save();
$profile->save();
Cache::forget('user:account:id:'.$user->id);
AccountService::del($profile->id);
return redirect('/settings/home')->with('status', 'Profile successfully updated!');
}
return redirect('/settings/home');
}
return redirect('/settings/home');
}
public function password()
{
return view('settings.password');
}
public function password()
{
return view('settings.password');
}
public function passwordUpdate(Request $request)
{
$this->validate($request, [
'current' => 'required|string',
'password' => 'required|string',
'password_confirmation' => 'required|string',
]);
public function passwordUpdate(Request $request)
{
$this->validate($request, [
'current' => 'required|string',
'password' => 'required|string',
'password_confirmation' => 'required|string',
]);
$current = $request->input('current');
$new = $request->input('password');
$confirm = $request->input('password_confirmation');
$current = $request->input('current');
$new = $request->input('password');
$confirm = $request->input('password_confirmation');
$user = Auth::user();
$user = Auth::user();
if (password_verify($current, $user->password) && $new === $confirm) {
$user->password = bcrypt($new);
$user->save();
if (password_verify($current, $user->password) && $new === $confirm) {
$user->password = bcrypt($new);
$user->save();
$log = new AccountLog();
$log->user_id = $user->id;
$log->item_id = $user->id;
$log->item_type = 'App\User';
$log->action = 'account.edit.password';
$log->message = 'Password changed';
$log->link = null;
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
$log = new AccountLog();
$log->user_id = $user->id;
$log->item_id = $user->id;
$log->item_type = 'App\User';
$log->action = 'account.edit.password';
$log->message = 'Password changed';
$log->link = null;
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
Mail::to($request->user())->send(new PasswordChange($user));
return redirect('/settings/home')->with('status', 'Password successfully updated!');
} else {
return redirect()->back()->with('error', 'There was an error with your request! Please try again.');
}
Mail::to($request->user())->send(new PasswordChange($user));
return redirect('/settings/home')->with('status', 'Password successfully updated!');
} else {
return redirect()->back()->with('error', 'There was an error with your request! Please try again.');
}
}
}
public function email()
{
return view('settings.email');
}
public function email()
{
return view('settings.email');
}
public function emailUpdate(Request $request)
{
$this->validate($request, [
'email' => 'required|email|unique:users,email',
]);
$changes = false;
$email = $request->input('email');
$user = Auth::user();
$profile = $user->profile;
public function emailUpdate(Request $request)
{
$this->validate($request, [
'email' => 'required|email|unique:users,email',
]);
$changes = false;
$email = $request->input('email');
$user = Auth::user();
$profile = $user->profile;
$validate = config_cache('pixelfed.enforce_email_verification');
$validate = config_cache('pixelfed.enforce_email_verification');
if ($user->email != $email) {
$changes = true;
$user->email = $email;
if ($user->email != $email) {
$changes = true;
$user->email = $email;
if ($validate) {
$user->email_verified_at = null;
// Prevent old verifications from working
EmailVerification::whereUserId($user->id)->delete();
}
if ($validate) {
// auto verify admin email addresses
$user->email_verified_at = $user->is_admin == true ? now() : null;
// Prevent old verifications from working
EmailVerification::whereUserId($user->id)->delete();
}
$log = new AccountLog();
$log->user_id = $user->id;
$log->item_id = $user->id;
$log->item_type = 'App\User';
$log->action = 'account.edit.email';
$log->message = 'Email changed';
$log->link = null;
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
}
$log = new AccountLog();
$log->user_id = $user->id;
$log->item_id = $user->id;
$log->item_type = 'App\User';
$log->action = 'account.edit.email';
$log->message = 'Email changed';
$log->link = null;
$log->ip_address = $request->ip();
$log->user_agent = $request->userAgent();
$log->save();
}
if ($changes === true) {
Cache::forget('user:account:id:'.$user->id);
$user->save();
$profile->save();
if ($changes === true) {
Cache::forget('user:account:id:'.$user->id);
$user->save();
$profile->save();
return redirect('/settings/home')->with('status', 'Email successfully updated!');
} else {
return redirect('/settings/email');
}
return redirect('/settings/email')->with('status', 'Email successfully updated!');
} else {
return redirect('/settings/email');
}
}
public function avatar()
{
return view('settings.avatar');
}
}
public function avatar()
{
return view('settings.avatar');
}
}

Some files were not shown because too many files have changed in this diff Show More